为何接口类型在Java方法重载中被视为完全不同类型?
这个问题其实是对《Effective Java》里那段关于重载的表述,误解了它的适用场景,咱们一步步拆解清楚:
首先明确书里原文的适用范围
书里提到的“完全不同的类型”,在那段话里是特指数组类型和其他类型的关系:
数组类型与除Object外的类类型完全不同;数组类型与除Serializable和Cloneable外的接口类型完全不同。
这句话的核心是说:数组类本身只继承了Object,并且只实现了Serializable和Cloneable这两个接口。所以对于任何其他的类或接口,你找不到一个非null的表达式,既能被转换为数组类型,又能被转换为那个类/接口类型——这才是“完全不同”的含义。比如String[]和Runnable就是完全不同的,没有任何对象能同时是数组又是Runnable的实现类,所以重载test(String[])和test(Runnable)不会有歧义。
你的例子为什么会出现歧义?
你写的代码是两个普通接口的重载方法,这两个接口类型并不满足“完全不同”的定义:因为存在非null的表达式(比如new Foo()),它可以同时被转换为Barable和Bazable(因为Foo同时实现了这两个接口)。这就不符合书里说的“每对重载方法中至少有一个对应形参的类型完全不同”的前提条件,所以自然会出现重载歧义。
再从Java重载解析的规则来看:当你调用test(f)时,f的类型是Foo,它转换为Barable或者Bazable都是身份转换(也就是不需要任何额外转换,直接兼容),这两种转换的优先级是完全相同的。编译器无法判断你想要调用哪一个重载方法,因此会抛出编译错误。
你的示例代码(编译报错):
interface Bazable { public void baz(); } interface Barable { public void bar(); } class Foo implements Barable, Bazable { public Foo() { } public void baz() { System.out.println("Can baz"); } public void bar(){ System.out.println("Can bar"); } } class Main { public static void main(String[] args) { Foo f = new Foo(); test(f); // 编译错误:对test的引用不明确 } public static void test(Barable t) { System.out.println("Using barable overload"); } public static void test(Bazable t) { System.out.println("Using bazable overload"); } }
总结
书里的结论并没有矛盾,只是你把“数组类型与接口类型的完全不同”,错误延伸到了“任意两个接口类型之间完全不同”。只有当两个重载方法的形参类型满足“没有任何非null表达式能同时转换为两者”时,才不会有歧义——而两个普通接口显然不满足这个条件,因为可以有类同时实现它们。
内容的提问来源于stack exchange,提问作者rb612

