如何从通配符类型变为参数化类型?
How to go from a wildcard type to a parameterized type?
假设有一个具有多个类型参数的框架类型 Row
,以及一个使用 Row
类型的实例并使用所有这些类型参数的方法。
我有一个方法适用于任何类型的 Row
,甚至可以同时适用于不同的类型,所以显然我使用的是通配符类型 Row<?,?>
。问题是,我如何调用一个采用 Row<R,K>
和 Row<?,?>
的方法?
我的思路:我不太清楚 Row<?,?>
是什么类型,但肯定是某种 Row
好吧。当一个泛型方法采用 Row<R,K>
时,这意味着它想用 R
和 K
做一些事情,但除此之外它可以处理任何类型的 Row
。所以我的 "any" 类型应该使用采用 "any" 类型的方法,对吗?
我在下面附上了我尝试过的示例代码。最奇怪的是最后一行确实有效,但它并不比我认为的任何其他内容更安全。所以基本上,如果可能的话,我想要一个比这更干净的解决方案,或者解释为什么要这样做。
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 有界类型对 R
和 [=14 的有效组合=]. Wrapper
然后提供了一个接受方法invokeNat
,它接受访问者并在R
和K
具体(不是通配符)的范围内执行copyRow
操作).
该技巧的灵感来自类别理论(因此名称中的 "natural"),并从 Scala 中导入,尽管 Scala 的类型模式匹配的当前实现允许更简洁的解决方案。已知此解决方案可在 .
下工作
假设有一个具有多个类型参数的框架类型 Row
,以及一个使用 Row
类型的实例并使用所有这些类型参数的方法。
我有一个方法适用于任何类型的 Row
,甚至可以同时适用于不同的类型,所以显然我使用的是通配符类型 Row<?,?>
。问题是,我如何调用一个采用 Row<R,K>
和 Row<?,?>
的方法?
我的思路:我不太清楚 Row<?,?>
是什么类型,但肯定是某种 Row
好吧。当一个泛型方法采用 Row<R,K>
时,这意味着它想用 R
和 K
做一些事情,但除此之外它可以处理任何类型的 Row
。所以我的 "any" 类型应该使用采用 "any" 类型的方法,对吗?
我在下面附上了我尝试过的示例代码。最奇怪的是最后一行确实有效,但它并不比我认为的任何其他内容更安全。所以基本上,如果可能的话,我想要一个比这更干净的解决方案,或者解释为什么要这样做。
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 有界类型对 R
和 [=14 的有效组合=]. Wrapper
然后提供了一个接受方法invokeNat
,它接受访问者并在R
和K
具体(不是通配符)的范围内执行copyRow
操作).
该技巧的灵感来自类别理论(因此名称中的 "natural"),并从 Scala 中导入,尽管 Scala 的类型模式匹配的当前实现允许更简洁的解决方案。已知此解决方案可在