Let's say there's a framework type Row
with multiple type parameters, and a method that works with instances of the Row
type and uses all these type parameters.
I have a method that works with any type of Row
, even different types at the same time, so obviously I'm using the wildcard type Row<?,?>
. The question is, how do I invoke a method that takes a Row<R,K>
with a Row<?,?>
?
My line of thought: I don't exactly know what type Row<?,?>
is, but it's surely some kind of Row
alright. And when a generic method takes Row<R,K>
it means that it wants to do something with R
and K
but otherwise it can deal with any type of Row
. So my "any" type should work with a method that takes "any" type, right?
I'm attaching sample code below with things that I tried. The weirdest thing is that the very last line actually works, but it's not any more type safe than anything else I think. So basically I'd like a cleaner solution than this if possible or an explanation why this is the way to go.
package foo;
public class Experiment {
// Defined by a framework.
interface Key<K extends Key<K>> {}
interface Row<R extends Row<R, K>, K extends Key<K>> {}
static <R extends Row<R, K>, K extends Key<K>> R copyRow(R row) {
return row;
}
// My experiments below.
static class Wrapper<R extends Row<R, K>, K extends Key<K>> {
public final R row = null; // fixme
public final Class<R> clazz = null; // fixme
}
static <R extends Row<R, K>, K extends Key<K>> R upcast(Row<?, ?> row) {
return (R) row;
}
static <R extends Row<R, K>, K extends Key<K>> R upcast(Row<?, ?> row, Class<R> clazz) {
assert row.getClass().equals(clazz);
return (R) row;
}
public static void main(String[] args) {
Wrapper<?, ?> wr = null; // fixme
copyRow(wr.row); // Compilation error
copyRow(upcast(wr.row)); // Compilation error
copyRow(upcast(wr.row, wr.clazz)); // This works, why?
}
}
(You can send this sample straight to javac to see what happens. With Java 1.8: https://pastebin.com/LB10ySsD)
Here is one possibility:
Essentially, the
copyRow
method is wrapped into a visitorNaturalRowTransformer
, which guarantees that it can handle all possible valid combinations of F-bounded type pairsR
andK
. TheWrapper
then provides an accepting methodinvokeNat
, which accepts the visitor and performs thecopyRow
operation in a scope whereR
andK
are concrete (not wildcards).The trick is inspired by category theory (hence the "natural" in the name), and imported from Scala, even though current implementations of Scala's pattern matching on types allow for more concise solutions. This solution is known to work under slightly more complex constraints.