Java泛型 - 使用泛型函数

时间:2016-07-01 13:38:20

标签: java generics

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

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:/项目/.../测试/ TestGenericsInheritance.java:[24,19]   test.TestGenericsInheritance.B不是抽象的,不会覆盖   抽象方法foo(java.lang.Object)in   test.TestGenericsInheritance.A   /D:/Projects/.../test/TestGenericsInheritance.java:[27,25]名称冲突:   foo(T)in test.TestGenericsInheritance .B和foo(T)in   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?

由于

1 个答案:

答案 0 :(得分:6)

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

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

类型擦除原因实现的原因是向后兼容性。我们的想法是新代码只使用泛型,而只有旧代码才会使用原始类型。因此,目标不是使原始类型最方便(因为新代码不应该使用它们),而是使原始类型最兼容。

考虑一下类ArrayList,它是在Java 2中引入的(在泛型之前)。它有一个方法public Object[] toArray(Object[] a)。在Java 5中引入了泛型,并且可以毫不费力地将此方法的签名更改为public <T> T[] toArray(T[] a)(其中T不是元素类型):因为实现了类型擦除的方式,对于旧代码使用(或子类)ArrayList而不是ArrayList<SomeType>方法签名保持不变。