为什么POP和PUSH方法的返回类型和参数类型分别是子类型而不是父类型?

假设我定义了一些类,如下所示:

public class Parent {

}

public class Child extends Parent {

}

public class ParentStack<E extends Parent> {
    private Object[] elements;
    private int size = 0;

    public ParentStack() {
        elements = new Object[3];
    }

    public void push(E e) {
        elements[size++] = e;
    }

    public E pop() {
        E result = (E) elements[size--];
        elements[size] = null;
        return result;
    }
}

public class ChildStack extends ParentStack<Child> {

    @Override
    public Child pop() {
        return super.pop();
    }

    @Override
    public void push(Child child) {
        super.push(child);
    }
}

在泛型擦除之后,返回和参数类型是ParentStack的父级

public class ParentStack {
    public void push(Parent e) {
        elements[size++] = e;
    }

    public E pop() {
        Parent result = (Parent) elements[size--];
        elements[size] = null;
        return result;
    }
}

因此,从理论上讲,ChildStack应该将类型定义为Parent而不是Child,否则,这些方法根本不会覆盖父方法.如何合理地解释这一点呢?

public class ChildStack extends ParentStack<Child> {

    @Override
    public Parent pop() {
        return super.pop();
    }

    @Override
    public void push(Parent child) {
        super.push(child);
    }
}

推荐答案

正如您可能知道的,对于pop,根本没有问题.返回类型不必完全匹配即可重写方法.该方法可以返回比它重写的方法更"特定"的类型.无论如何调用pop,呼叫者都会得到"是"Parent的东西.


就Java language而言,泛型类型exist,因此ChildStack中的push确实覆盖了ParentStack中的泛型类型.另请参阅overriding rules.编译器理解泛型,因此它可以强制执行这些规则.否则对程序员来说会很不方便,不是吗?他们将不得不编写一个额外的方法:

@Override
public void push(Parent p) {
    this.push((Child)p);
}

是Java虚拟机不理解泛型类型.就它而言,ParentStack中的pushpush(Parent),而您在ChildStack中写的push(Child)不会覆盖它.

编译器如何将您的代码编译为JVM能够理解的内容,同时实现覆盖规则?它会秘密地生成一个额外的push(Parent) for you(类似于我上面写的那个).这就是众所周知的bridge method.就JVM而言,桥方法覆盖ParentStack.push,而不是您编写的push(Child)方法.

Java相关问答推荐

OpenJDK、4K显示和文本质量

SQlite for Android无法使用json_group_array/json_object

ittext pdf延迟签名,签名无效

为什么我的ArrayList索引的索引总是返回-1?

确定Java中Math.Ranb()输出的上限

使用意向过滤器从另一个应用程序启动服务

DTO到实体,反之亦然,控制器和服务之间的哪一层应该处理转换?

内存中的H2修剪尾随空格

第二次按下按钮后,我需要将按钮恢复到其原始状态,以便它可以再次工作

使用UTC时区将startDatetime转换为本地时间

JOOQ中的子查询使用的是默认方言,而不是配置的方言

如何在Spring Java中从数据库列中获取JSON中的具体数据

Java构造函数分支

找出承载Cargo 的最小成本

如何在运行docker的应用程序中获取指定的配置文件

Win32函数的JNA绑定DwmGetColorizationColor返回E_INVALIDARG错误

从字节数组切换到JakartaMail org.springframework.mail.javamail.JavaMailSender InputStreamResource

由于版本不匹配,从Java 8迁移到Java 17和Spring 6 JUnit4失败

Java 21保护模式的穷尽性

在JPanel上使用GridBagLayout并将JButton放在里面时出现问题