为什么我的原始类型争论方法不会覆盖包装类型争论的超类方法?
public class WrapperClasses{ void overloadedMethod(Number N){ System.out.println("Number Class Type"); } void overloadedMethod(Double D){ System.out.println("Double Wrapper Class Type"); } void overloadedMethod(Long L){ System.out.println("Long Wrapper Class Type"); } public static void main(String[] args){ int i = 21; WrapperClasses wr = new WrapperClasses(); //wr.overloadedMethod(i); } } class mine extends WrapperClasses{ void overloadedMethod(int N){ System.out.println("Integer Class Type"); } public static void main(String[] args){ int i = 21; WrapperClasses wr = new mine(); wr.overloadedMethod(i); } }
这将打印Number Class Type
。
我理解包装类方法重载的规则:
- 如果要将原始数据类型作为参数传递给方法调用,则编译器首先检查采用与参数相同的数据类型的方法定义。
- 如果不存在这样的方法,则它检查方法定义,该方法定义采用比传递的数据类型更大的原始数据类型。 即,它尝试对传递的数据类型执行自动扩展转换。
- 如果无法进行自动扩展转换,则它会检查一个方法定义,该定义将相应的包装类类型作为参数。 即,它尝试执行自动装箱转换。
- 如果这样的方法不存在,那么它会检查一个以超类类型(Number或Object类型)作为参数的方法。
- 如果这样的方法也不存在,那么编译器会给出编译时错误。
根据规则1,它应该打印Integer Class Type
。 我在这里想念的是什么?
在语言规范级别,这是因为具有作为基元和包装基元类型不同的参数的方法不被视为重写等效 。 (一种奇特的说法,“他们只是不这样做,因为语言规范这么说”)。
但从逻辑上讲,它们也不应该,至少在子类中的int
参数“覆盖”超类中的包装参数的情况下。
根据Liskov的替换原则,子类中的方法必须至少接受超类中方法接受的所有参数。
如果超类方法接受包装类,则它可以接受null
。 如果允许子类方法只接受int
,则它不能接受null,因此它不可替代。
方法重载决策是在编译时根据保存对您调用方法的实例的引用的变量的编译时类型确定的。
void overloadedMethod(int N)
仅在我的子类中定义。 因此,当您在类型为基类WrapperClasses
的引用上调用方法时,只能考虑基类的方法来进行重载解析。
传递给方法的int
参数与基类的3个方法中的任何一个都不匹配,但在将其装箱为Integer
,它与void overloadedMethod(Number N)
方法匹配。
如果您将代码更改为
int i = 21; mine wr = new mine(); wr.overloadedMethod(i);
它将执行void overloadedMethod(int N)
。
多态性由JVM在运行时执行。 为此,两个方法必须具有相同的运行时签名。
在协变返回类型的情况下,编译器生成桥接方法以允许这种情况发生,但是,Java语言规范不需要包装器与原语的这种桥接方法。
这可能有很多原因,但最有可能是向后兼容性。 Java 1.0没有这样做,即使十多年后添加了自动装箱,也不允许破坏旧代码。 即包装和原语相互重载,而不是覆盖,所以他们现在不能。