多个重载方法:null是否等于NullPointerException?

25
public class TestMain {

    public static void methodTest(Exception e) {
        System.out.println("Exception method called");
    }

    public static void methodTest(Object e) {
        System.out.println("Object method called");
    }

    public static void methodTest(NullPointerException e) {
        System.out.println("NullPointerException method called");
    }

    public static void main(String args[]) {
        methodTest(null);
    }   
}

输出:NullPointerException方法被调用


6
如果你使用 methodTest((Object)null);,会发生什么?或者 methodTest((Exception)null); 会怎样? - MadProgrammer
1
如果使用 methodTest((Object)null),则输出为“调用了 Object 方法”。 如果使用 methodTest((Exception)null),则输出为“调用了 Exception 方法”。 - Anil Gowda
所以 null 不是 NullPointerException,为什么编译器在调用模糊不清的方法时选择调用它,我不知道。 - MadProgrammer
这里真的应该有一个编译器警告… - helb
编译器警告没有被触发,使用methodTest(null)应该是可以的。然而,methodTest((Object)null)和methodTest((Exception)null)应该会得到编译器警告,但是即使这样也没有出现。 - Anil Gowda
显示剩余2条评论
2个回答

38

如果有几个过载的方法可能会使用给定参数进行调用(在这种情况下是null),编译器将选择最具体的方法。

请参阅http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2.5

在您的情况下,methodTest(Exception e)methodTest(Object e)更具体,因为Exception是Object的子类。而methodTest(NullPointerException e)则更为具体。

如果您将NullPointerException替换为另一个Exception的子类,则编译器将选择该子类。

另一方面,如果您添加像testMethod(IllegalArgumentException e)这样的额外方法,编译器将会抛出错误,因为它不知道选择哪个方法。


2
@Runcorn 你真的试过那个吗?它按照我在这里的回答所描述的那样工作。 - Thomas Stets
5
NullPointerException并不比null更具体,它比Exception更具体,这意味着对于这个异常来说,比起Exception,只有一个更小的参数子集是有效的。在所有三种方法中,null只是可能出现的一个值。 - Thomas Stets
2
@PatrickCollins:你使用“继承”这个词的方式有些奇怪;当派生类型具有基本类型的所有成员时,一种类型继承自另一种类型。我认为你使用“继承”来表示“隐式转换”,这是完全不同的事情。Null可以隐式转换为许多类型,但这并不意味着null表达式是拥有每个其他类型的所有成员的类型。 - Eric Lippert
2
@PatrickCollins:针对我们的问题“null将转换为离对象最远的内容?”--有点像。更好的思考方式是:null可以转换为许多类型。在这些转换中,有些比其他的更好。转换为更具体的类型比转换为不太具体的类型更好。重载分辨率的工作是根据各种转换之间可能存在的--可能相互矛盾的--“更好”关系来推断出最佳方法。 - Eric Lippert
1
@PatrickCollins:例如,如果您有类型Object、Animal、Turtle和Banana,并且它们之间存在明显的关系,并且有重载函数接受Object、Turtle和Banana,那么Turtle并不会胜出,因为它距离Object比Banana更远。相反,两者都不会胜出。Turtle比Object更具体,因此排除了Object,但是Turtle和Banana都不比另一个更具体,因此两者都不会是“最佳”的,重载解析将会报错。 - Eric Lippert
显示剩余8条评论

9
编译器会尝试匹配最具体的参数,本例中为NullPointerException。您可以在Java语言规范的第15.12.2.5章节中了解更多信息,选择最具体方法:
如果有多个成员方法对方法调用既可访问又适用,则需要选择一个来提供运行时方法分派的描述符。Java编程语言使用的规则是选择最具体的方法。
非正式的直觉是,如果第一个方法处理的任何调用都可以传递给另一个方法而不会出现编译时类型错误,则一个方法比另一个方法更具体。

在 Joshua Bloch 和 Neal Gafter 的《Java Puzzlers》一书中,有一个谜题专门讲解了“最具体方法”行为。 - Timmos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接