Java 通常可以根据参数(甚至根据返回类型,与 C# 等不同)推断泛型。
举个例子:我有一个泛型类 Pair
它只存储一对值,可以按以下方式使用:
Pair<String, String> pair = Pair.of("Hello", "World");
方法 of
看起来就像这样:
public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) {
return new Pair<T1, T2>(first, second);
}
非常好。 但是,这不再适用于以下需要通配符的用例:(
Pair<Class<?>, String> pair = Pair.of((Class<?>) List.class, "hello");
请注意显式转换以使 List.class
成为正确的类型。)
代码失败并出现以下错误(由 Eclipse 提供) ):
类型不匹配:无法从 TestClass.Pair,String>
转换为 TestClass.Pair,String>< /代码>
但是,显式调用构造函数仍然可以按预期工作:
Pair<Class<?>, String> pair =
new Pair<Class<?>, String>((Class<?>) List.class, "hello");
有人可以解释这种行为吗? 是设计使然吗? 这是想要的吗? 我是否做错了什么,或者我是否偶然发现了设计中的缺陷/编译器中的错误?
疯狂猜测:“捕获#1-of?” 不知何故似乎暗示通配符是由编译器动态填充的,使类型成为 Class
,从而导致转换失败(来自 Pair ,字符串>
到Pair,字符串>
)。 这是正确的吗? 有办法解决这个问题吗?
为了完整起见,下面是 Pair
类的简化版本:
public final class Pair<T1, T2> {
public final T1 first;
public final T2 second;
public Pair(T1 first, T2 second) {
this.first = first;
this.second = second;
}
public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) {
return new Pair<T1, T2>(first, second);
}
}
Java can often infer generics based on the arguments (and even on the return type, in contrast to e.g. C#).
Case in point: I've got a generic class Pair<T1, T2>
which just stores a pair of values and can be used in the following way:
Pair<String, String> pair = Pair.of("Hello", "World");
The method of
looks just like this:
public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) {
return new Pair<T1, T2>(first, second);
}
Very nice. However, this no longer works for the following use-case, which requires wildcards:
Pair<Class<?>, String> pair = Pair.of((Class<?>) List.class, "hello");
(Notice the explicit cast to make List.class
the correct type.)
The code fails with the following error (provided by Eclipse):
Type mismatch: cannot convert from TestClass.Pair<Class<capture#1-of ?>,String>
to TestClass.Pair<Class<?>,String>
However, explicitly calling the constructor still works as expected:
Pair<Class<?>, String> pair =
new Pair<Class<?>, String>((Class<?>) List.class, "hello");
Can someone explain this behaviour? Is it by design? Is it wanted? Am I doing something wrong or did I stumble upon a flaw in the design / bug in the compiler?
Wild guess: the “capture#1-of ?” somehow seems to imply that the wildcard is filled in by the compiler on the fly, making the type a Class<List>
, and thus failing the conversion (from Pair<Class<?>, String>
to Pair<Class<List>, String>
). Is this right? Is there a way to work around this?
For completeness’ sake, here is a simplified version of the Pair
class:
public final class Pair<T1, T2> {
public final T1 first;
public final T2 second;
public Pair(T1 first, T2 second) {
this.first = first;
this.second = second;
}
public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) {
return new Pair<T1, T2>(first, second);
}
}
发布评论
评论(1)
构造函数起作用的原因是您显式指定类型参数。 如果您这样做,静态方法也将起作用:
当然,您首先拥有静态方法的全部原因可能只是为了获得类型推断(这根本不适用于构造函数)。
这里的问题(正如您所建议的)是编译器正在执行 捕获转化。 我相信这是由于 [JLS §15.12.2.6]:
如果您确实想要推断,一种可能的解决方法是执行以下操作:
变量
pair
将具有更宽的类型,这确实意味着需要更多地输入变量的类型名称,但至少您不再需要在方法调用中进行强制转换。The reason the constructor works is that you're explicitly specifying the type parameters. The static method also will work if you do that:
Of course, the whole reason you have a static method in the first place is probably just to get the type inference (which doesn't work with constructors at all).
The problem here (as you suggested) is that the compiler is performing capture conversion. I believe this is as a result of [§15.12.2.6 of the JLS]:
If you really want the inference, one possible workaround is to do something like this:
The variable
pair
will have a wider type, and it does mean a bit more typing in the variable's type name, but at least you don't need to cast in the method call anymore.