代码之家  ›  专栏  ›  技术社区  ›  Xenon

为什么Java泛型类可以实现具有对象参数的泛型接口的方法?

  •  0
  • Xenon  · 技术社区  · 6 年前

    下面是Java代码:

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

    尽管我认为类不会重写接口中的方法,但它确实可以编译。通过类型推断,我假设类型参数t始终等于对象,因为类从接口实现方法,在接口中它清楚地将对象声明为参数。重写方法中带有(t yes)的版本也可以工作,但这对我来说是显而易见的。你能告诉我为什么我举的例子是这样的吗?

    谢谢您。

    3 回复  |  直到 6 年前
        1
  •  4
  •   rgettman    6 年前

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

    通常,人们会期望重写方法的签名必须完全匹配。事实并非如此。虽然精确匹配签名肯定会重写该方法,但是 擦除 的签名也将重写该方法。这个 JLS, Section 8.4.8.1 ,状态:

    实例方法m C 在类C中声明或由类C继承,重写来自C的另一个方法M 在A类中声明,如果以下所有内容都是正确的:

    ——剪断

    M的签名 C 是M签名的子签名(§8.4.2) .

    section 8.4.2 状态:

    方法m的签名 是一个 子签名 方法m的签名 如果其中一个:

    • 与M具有相同的签名

    • M的签名 与M签名的删除(_§4.6)相同。 .

    因为删除了 T Object ,允许使用 对象 . 如果在 T 在接口中,则不能再用 对象 一定是上界,即擦除。

    // 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 使用参数类型的父类型的方法,例如 对象 .

    // A compiler error occurs here when the erasure is Number.
    @Override
    public void doSomething(Object yes) {
    
    }
    
        2
  •  1
  •   StefTheDrummer    6 年前

    所以你偶然发现了协方差和反方差。 假设你有:

    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型参数o 保证为整数 -gt; o 也保证是数字或对象 ->因为整数扩展数字扩展对象

    所以你在削弱这一类型! 这就是您的代码所做的。

        3
  •  0
  •   Xenon    6 年前

    我可能发现了 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编译器用对象替换它:

    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; }
        // ...
    }
    

    在以下示例中,泛型节点类使用有界类型参数:

    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,可比:

    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。