为什么Java泛型类可以使用Object参数实现泛型接口的方法?

时间:2018-10-22 17:24:20

标签: java generics interface

这是Java代码:

public interface myInterface<T> {
    void doSomething(T yes);
}

private static class myInterfaceImpl<T> implements myInterface<T>{
    @Override
    public void doSomething(Object yes) {

    }
}

即使我认为该类没有从接口重写该方法,也可以编译该东西。通过类型推断,我将假定类型参数T始终等于Object,因为该类从接口实现方法,在接口中它明确将Object声明为参数。覆盖方法中带有(T yes)的版本也可以使用,但这对我来说很明显。您能告诉我我所举的例子为什么如此吗?

谢谢。

3 个答案:

答案 0 :(得分:4)

在这里,类型擦除是Java在JDK 1.5中引入泛型时用于向后兼容的机制,使事情有点违反直觉。

通常,人们希望重写方法的签名必须完全匹配。事实并非如此。虽然完全匹配签名肯定会覆盖该方法,但签名的 erasure 也将会覆盖该方法。 JLS, Section 8.4.8.1指出:

  

在类C中声明或继承的实例方法m C ,从C重写在类A中声明的另一个方法m A ,前提是以下所有条件均成立:

-snip

  

m C 的签名是m A 的签名的子签名(第8.4.2节)。

然后section 8.4.2指出:

  

方法m 1 的签名是方法m 2 的签名的子签名

     
      
  • m 2 与m 1

  • 具有相同的签名   
  • m 1 的签名与m 2 的签名的擦除(第4.6节)相同。

  •   

由于对T的擦除为Object,因此允许使用采用Object的方法重写(在此实现)该方法。如果您在界面中的T上设置了上限,那么您将无法再使用Object覆盖该方法。一定是那个上限,就是擦除。

// Example of erasure with upper bound
interface myInterface<T extends Number> {
    void doSomething(T yes);
}

class myInterfaceImpl<T extends Number> implements myInterface<T>{
    @Override
    public void doSomething(Number yes) {

    }
}

请注意,此处不允许参数相反。使用上限时,您不能使用采用参数类型超类型的方法来覆盖doSomething,例如Object

// A compiler error occurs here when the erasure is Number.
@Override
public void doSomething(Object yes) {

}

答案 1 :(得分:1)

因此,您偶然发现了协方差和相反方差。 假设您有:

IMyInterface<T> {
  void foo(T o);
}

然后您实现

class A implements IMyInterface<Integer> {
  void foo(Integer o) { ... }
}

您也可以写:

class A implements IMyInterface<Integer> {
  void foo(Number o) { ... }
}

class A implements IMyInterface<Integer> {
  void foo(Object o) { ... }
}

因为类型T的 param o 被保证为整数 -> o 也保证是数字或对象 ->因为整数扩展了数字扩展了对象

所以您正在减弱类型! 这就是您的代码。

答案 2 :(得分:0)

我可能已经找到了answer

在类型擦除过程中,Java编译器将擦除所有类型参数,如果类型参数是有界的,则将每个参数替换为其第一个边界。如果类型参数是无界的,则将其替换为对象。

请考虑以下表示单个链接列表中节点的通用类:

public class Node<T> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

由于类型参数T是无界的,因此Java编译器将其替换为Object:

public class Node {

    private Object data;
    private Node next;

    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Object getData() { return data; }
    // ...
}

在下面的示例中,通用Node类使用一个有界的类型参数:

public class Node<T extends Comparable<T>> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

Java编译器将绑定类型参数T替换为第一个绑定类Comparable:

public class Node {

    private Comparable data;
    private Node next;

    public Node(Comparable data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Comparable getData() { return data; }
    // ...
}

感谢@Oleksandr。