覆盖方法中的泛型
Posted
技术标签:
【中文标题】覆盖方法中的泛型【英文标题】:Generics in overridden methods 【发布时间】:2013-04-04 04:04:20 【问题描述】:遇到了一个有趣的问题;以下类编译:
public class Test
public static void main(String[] args) throws Exception
A a = new A();
B b = new B();
foo(a);
foo(b);
private static void foo(A a)
System.out.println("In A");
private static void foo(B b)
System.out.println("In B");
private static class A
private static class B extends A
但是这个失败了:
public class Test
public static void main(String[] args) throws Exception
A<String> a = new A<>();
B b = new B();
foo(a);
foo(b);
private static void foo(A<String> a)
System.out.println("In A");
private static void foo(B b)
System.out.println("In B");
private static class A<T>
private static class B extends A
出现此错误:
Test.java:8: error: reference to foo is ambiguous, both method foo(A<String>) in Test and method foo(B) in Test match
foo(b);
^
Note: Test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error
我原以为由于类型擦除,这些将基本相同。有人知道这里发生了什么吗?
【问题讨论】:
A<String> a = new A<>();
是什么? A<String> a = new A<String>();
怎么样?
@Trinimon 这只是 Java7 A原因是您混合了泛型和原始类型(B 应声明为 class B<T> extends A<T>
或 class B extends A<SomeType>
)。
发生这种情况的实际原因被埋在the JLS, section #15.12.2.7 的某个地方,然后 - 祝你好运,简洁地表达它;-)
【讨论】:
与@Louis 的回答相同。仅靠这种变化是不够的。 我认为#15.12.2.5 部分在这里可能更适用。编译器不会试图推断类型;它试图决定调用哪个方法。在我看来,问题应该是,为什么编译器不认为foo(B)
比foo(A<String>)
更具体,因为它知道b
是B
类型?
我看起来可能是这些规范的某种组合,15.12.2.5 引用了使用 15.12.2.7 中的过程推断的类型。挖掘那些我绝对不是他们的符号的粉丝 - 似乎他们可以选择更具表现力的东西。
@matts 是的,确实可以归结为找出一种方法是否比另一种更具体,这取决于 15.2.2.7 何时涉及泛型。老实说,我没有仔细看,因为它真的很乱。【参考方案2】:
在泛型出现之前,Java 有类似的方法
public class Collections
public void sort(List list) ... [1]
而用户代码可能有类似的东西
public class MyList implements List ... [2]
MyList myList = ...;
Collections.sort(myList); [3]
当泛型被添加到 Java 中时,我们决定将现有类和方法转换为泛型类和方法,而不会破坏任何使用它们的代码。就难度而言,这是一项了不起的成就,但代价是让语言变得复杂和有缺陷。
所以[1]
已泛化,但[3]
仍必须按原样编译,而不必泛化[2]
。
黑客在§15.12.2.3
可以通过方法调用转换(§5.3)将Ai转换为Si
基本上说如果参数类型(Ai)是原始的,那么为了匹配的目的也删除参数类型(Si)。
回到你的例子,我们知道为什么foo(A<String>)
被认为适用于foo(b)
。
但是还有另一个问题 - foo(A<String>)
是否适用于 [§15.12.2.2]?从规范的字母来看,答案似乎是“不”。但这可能是规范的错误。
【讨论】:
对,foo(AB
不是A<String>
的子类型,因此foo(B)
并不比foo(A<String>)
更具体
那么,奇怪的是 B 被认为是 Aprivate static class B extends A
您在此处省略了 A
的类型参数。你的意思可能是
private static class B<T> extends A<T>
此外,B b = new B()
也没有 <String>
参数;你必须这样做
B<String> b = new B<String>();
...最后,
private static void foo(B b)
System.out.println("In B");
应该是这样的
private static void foo(B<String> b)
System.out.println("In B");
一般来说,如果有一个类型 Foo
有一个泛型参数,Foo
基本上应该总是有一个类型参数。在这种特殊情况下,class B extends A
没有A
的类型参数,然后B
需要一个类型参数,因此您在提到B
的任何地方都需要一个类型参数。 (该规则的一个主要例外是 instanceof
表达式,但这里没有。)
【讨论】:
是的,在不指定类型参数的情况下引用参数化类型绝对是禁忌。我遇到了一些扩展 java.util 集合接口的旧类,这些接口从未更新为处理泛型。问题更多的是当泛型应用于 A 时,类型推断规则有什么不同。以上是关于覆盖方法中的泛型的主要内容,如果未能解决你的问题,请参考以下文章