imagej / imagej-ops

ImageJ Ops: "Write once, run anywhere" image processing
https://imagej.net/libs/imagej-ops
BSD 2-Clause "Simplified" License
90 stars 42 forks source link

Force built-in methods to demand the needed return type #509

Open ctrueden opened 7 years ago

ctrueden commented 7 years ago

Consider the following ops:

@Plugin(type = Op.class, priority = LOW)
OrangeEaterOp implements FunctionOp<Orange, OrangePeel> { ... }

@Plugin(type = Op.class, priority = HIGH)
AppleEaterOp implements FunctionOp<Apple, AppleCore> { ... }

These currently would have following built-in methods in the FruitNamespace:

AppleCore eat(Apple a) {
  return (Apple) match(a).apply(a)
}

OrangePeel eat(Orange o) {
 return (Orange) match(o).apply(o)
}

Now suppose we have a hybrid type:

Orple implements Apple, Orange { ... }

And we run the following code:

OrangePeel peel = ops.fruit().eat((Orange) myOrple);

It will match the wrong op, and thus then cast the result to the wrong return type.

This is a danger with how the type-safe namespace signatures are currently set up. We made the decision to allow the ops matcher some flexibility in which op gets matched. However, they do not constrain the return type to match that expected, nor even test it before casting. Fortunately, the Ops matcher can constrain the return type, so let's do that!