【问题标题】:Java Generics - extending generic class with generic functionJava 泛型 - 用泛型函数扩展泛型类
【发布时间】:2016-07-01 13:38:19
【问题描述】:

我有一个这样的简单程序:

package test;

public class TestGenericsInheritance {

    public static void main(String[] args) {}

    public static abstract class A<Q>{

        public void foo2(Q obj){}
        public abstract <T> void foo(T obj);
    }

    public static class C extends A<Object>{

        @Override
        public <T> void foo(T obj) {}
    }

    public static class B extends A{

        @Override
        public <T> void foo(T obj) {}
    }
}

如您所见,它完全没有任何作用。在 java 1.6 和 1.7 上编译此程序文件时出现以下错误:

/D:/Projects/.../test/TestGenericsInheritance.java:[24,19] test.TestGenericsInheritance.B 不是抽象的,也不会覆盖 抽象方法 foo(java.lang.Object) 在 test.TestGenericsInheritance.A /D:/Projects/.../test/TestGenericsInheritance.java:[27,25] 名称冲突: foo(T) 在 test.TestGenericsInheritance .B 和 foo(T) 在 test.TestGenericsInheritance.A 具有相同的擦除,但两者都没有 覆盖另一个 /D:/Projects/.../test/TestGenericsInheritance.java:[26,9] 方法确实 不覆盖或实现超类型中的方法

类 C 和 B 在语义上是相同的,但是类 B 不将方法 foo 识别为 A#foo 的实现。为了使此代码兼容,我必须在 B 类中使用以下签名实现方法 A#foo:

public void foo(Object obj) 

我的问题是,为什么我的程序无法编译?泛型 Q 和 T 是完全独立的,为什么只有在为继承的类 A 显式指定泛型 Q 时才能实现泛型函数 A#foo?

谢谢

【问题讨论】:

  • 我可以复制。更重要的是同时拥有非泛型方法和泛型方法会导致具有相同类型擦除的错误。
  • Eclipse 建议添加:@Override public void foo(Object obj) {}B,这实际上使它可以编译。 (1.8)
  • @4castle 您的编辑使错误消息令人困惑,因为它们提到了您已删除的源文件名。
  • @KlitosKyriacou 不是我删除的。之前是主编。我会回滚。
  • @4castle 很抱歉责怪你,确实是你之前的那个人。这是一个有趣的问题,正如下面的答案所示,你应该尽量避免使用原始类型。

标签: java generics


【解决方案1】:

B 扩展了原始类型 A。因为您使用原始类型,所以所有通用信息都将被删除,而不仅仅是您未能指定的类型变量(请参阅Section 4.6 of the Java Language Specification)。这意味着A 有一个方法void foo(Object obj),而A&lt;SomeType&gt; 有一个方法&lt;T&gt; void foo(T obj)

如果你重写一个方法,它必须具有相同的签名(可选地在对重写的方法进行类型擦除之后)——有趣的是,重写的方法可能有一个不同的、更具体的返回类型。您的两种方法的签名不同(您需要在覆盖方法中进行类型擦除),因此您的示例无法编译。

按原样实现类型擦除的原因是向后兼容。这个想法是新代码将只使用泛型,而只有旧代码将使用原始类型。因此,目标不是让原始类型最方便(因为无论如何新代码都不应该使用它们),而是让原始类型最兼容。

ArrayList 类为例,它是在 Java 2 中引入的(远在泛型之前)。它有一个方法public Object[] toArray(Object[] a)。在 Java 5 中引入了泛型,并且该方法的签名可以毫无困难地更改为 public &lt;T&gt; T[] toArray(T[] a)(其中 T 不是元素类型):由于实现类型擦除的方式,对于使用的旧代码(或子类)ArrayList 而不是 ArrayList&lt;SomeType&gt; 方法签名保持不变。

【讨论】:

  • 我还是不明白。 “所有通用信息都被删除”出现在规范中的什么位置?
  • 它说:“类型擦除还将构造函数或方法的签名(第 8.4.2 节)映射到没有参数化类型或类型变量的签名。”没有提及任何附加条件。
  • @Hoopje 谢谢你的解释。我明白我的问题背后的原因是什么。但是,在这种情况下,我认为删除所有泛型类型没有意义。我知道应该删除类型参数。但是,如果方法参数独立于类型参数,为什么还要擦除它呢?您能否提供任何需要这种擦除的情况?
  • @MAREK 我更新了答案以回答这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-28
  • 2012-07-24
相关资源
最近更新 更多