我刚刚看到这个c#问题,想知道Java中是否会发生类似的事情。可以,用
class A<T> {
A(Integer o) {...}
A(T o) {...}
}
调用
new A<Integer>(43);
是模糊的,我看不出如何解决它。有吗?
您可以在构造期间删除泛型(并抑制警告):
A<Integer> a = new A(42);
或者,不太理想地使用反射(在这种情况下,您必须再次抑制警告)
Constructor<A> c = A.class.getDeclaredConstructor(Integer.class);
A<Integer> a = c.newInstance(42);
是的,参数化类型JLS3#4.5.2的成员可能会发生冲突,而这种冲突在正常的类声明(#8.4.8)中是不会发生的。这类例子很容易举出。
在Java中,你的例子中的构造函数没有一个比另一个更具体,因为T
和Integer
之间没有子类型关系。参见Reference与泛型有歧义
如果方法重载造成了这种歧义,通常可以选择使用不同的方法名。但是构造函数不能重命名。
更多的诡辩:
如果<T extends Integer>
,则T
确实是Integer
的子类型,则第二个构造函数比第一个构造函数更具体,将选择第二个构造函数。
实际上javac不允许这两个构造函数共存。当前Java语言规范中没有禁止它们,但是字节码中的限制迫使javac禁止它们。参见Java中的类型擦除和重载:为什么会这样?
另一点:如果<T extends Integer>
,因为Integer
是final
,所以T只能是Integer
,所以Integer
也必须是T
的子类型,因此第二个构造函数不是也比第一个构造函数更具体吗?
。在子类型关系中不考虑final
。实际上有一天从Integer
中删除final
是可能的,Java甚至指定删除final
不会破坏二进制兼容性。
实际上,它是不明确的,因此如果您尝试new A<Integer>(new Integer(0))
,则无法编译。