如何从通配符类型变为参数化类型?



假设有一个具有多个类型参数的框架类型Row,以及一个处理Row类型实例并使用所有这些类型参数的方法。

我有一种适用于任何类型的Row的方法,甚至同时使用不同类型的方法,所以显然我使用的是通配符类型Row<?,?>.问题是,如何调用一个采用Row<R,K>的方法Row<?,?>

我的想法:我不完全知道Row<?,?>是什么类型,但它肯定是某种Row好的。当通用方法需要Row<R,K>时,这意味着它想对RK做一些事情,但除此之外,它可以处理任何类型的Row.所以我的"任何"类型应该与采用"任何"类型的方法一起使用,对吧?

我将在下面附加示例代码以及我尝试过的东西。最奇怪的是,最后一行实际上有效,但它并不比我认为的其他任何东西都更安全。所以基本上,如果可能的话,我想要一个比这更干净的解决方案,或者解释为什么这是要走的路。

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?
}
}

(您可以将此示例直接发送到javac以查看会发生什么。使用 Java 1.8:https://pastebin.com/LB10ySsD(

这是一种可能性:

public class WildcardsExperiment {
// Defined by a framework.  <begin unmodifiable>
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;
}
// <end unmodifiable>
interface NaturalRowTransformer {
<R extends Row<R, K>, K extends Key<K>> R apply(R r);
}
class Wrapper<R extends Row<R, K>, K extends Key<K>> {
private final R row = null; // fixme (constructor etc.)
public final Class<R> clazz = null; // fixme
R invokeNat(NaturalRowTransformer nat) {
return nat.apply(row);
}
}
static final NaturalRowTransformer CopyRow = new NaturalRowTransformer() {
public <R extends Row<R, K>, K extends Key<K>> R apply(R row) {
return copyRow(row);
}
};
public static void main(String[] args) {
Wrapper<?, ?> wr = null; // fixme
wr.invokeNat(CopyRow); // compiles
}
}

本质上,copyRow方法被包装到一个访问者NaturalRowTransformer中,这保证了它可以处理 F 边界类型对的所有可能的有效组合RK。然后,Wrapper提供了一个接受方法invokeNat,该方法接受访问者并在RK具体(不是通配符(的作用域中执行copyRow操作。

这个技巧的灵感来自范畴理论(因此名称中的"自然"(,并从Scala导入,尽管Scala在类型上的模式匹配的当前实现允许更简洁的解决方案。众所周知,此解决方案可在稍微复杂的约束下工作。

最新更新