如何使方法采用空值?

How can I make a method take a null value?

如果你有两个像这样的重载方法:

public void methodName(File file){}
public void methodName(String string){}

如果您尝试使用 null 调用 methodName,您将收到一条错误消息,指出它不明确,这是可以理解的,因为它不知道要执行哪种方法。

我知道你可以只转换 null: methodName((String) null) 但我如何创建一个方法来专门处理你调用 methodName(null) 的情况?

像这样:

public void methodName(null null){}

如何创建一个必须为 null 的方法?

只做一个没有任何参数的方法。

public void methodName(){}

要求 must take a null 的方法与要求始终采用 5 的方法或始终采用 "foo" 的方法相同。如果传递的参数应该始终包含相同的值,则根本不需要该参数。

并且如果您要求的是只要传递的参数为 null 就会被选择的方法,无论其类型如何(即下面的两个方法调用都会调用相同的重载方法),

File f = null;
String s = null;
methodName (f);
methodName (s);

这是不可能的,因为要使用的重载方法必须在编译时使用参数的编译时类型来选择。在编译时,编译器无法知道执行方法时传递的变量是否包含 null。

传递的参数可以始终始终为空如果它是Object。当您在空引用上尝试 运行 一个方法时,您会得到一个空指针异常。

所以,public void methodName(File file){}可以无一例外地称为methodName(null)

然而,

public void methodName(File file) {
    file.delete();
}

传null会导致空指针异常

如您所见,当您传递 null.

时,编译器无法解析采用不同类型对象的两种方法

解决此问题的唯一方法是像您所做的那样进行类型转换,或者使用一种采用通用 Object 类型并尝试向下转换的方法:

public void methodName( Object object ) {
    if ( object == null ) {
        // do something with null
    } else if ( object instanceof File ) {
        // do something with ((File)object)
    } else {
        // do something else
    }
}

编写看起来像这样的代码往往被认为是臭的,这是有充分理由的。它很快变得复杂,难以维护等。最好的办法是进行类型转换或更改方法签名,以便您(和编译器)始终知道应该调用哪个函数来处理特定的 null 对象。

您不能编写专门采用 null 的方法。你必须做这样的事情:

methodName(File file) {
   if(file == null) {
      someOtherMethod();
   }
   else {
      // other stuff
   }
}

但更常见的做法是让 methodName(File) 处理 null,并记录它对 null 的作用。如果你让它调用另一个方法并且 methodName(File)someOtherMethod() 不是最终的,你应该记录内部调用。

你不能这样做。请记住,您可能有隐式 null。例如:

File file = null;
methodName(file);

然而,Java编译器必须link调用特定的方法签名,所以它应该在编译时知道null是否被传递。

另一方面,为什么要坚持 null?没有什么能阻止您定义特殊类型:

enum Null { NULL }
public void methodName(File file){}
public void methodName(String string){}
public void methodName(Null n) {}

methodName(Null.NULL); // or methodName(NULL); with import static

有一种类型常用于表示 null,这是 Void,其中唯一有效值是 null

你可以写

void methodName(Void v);

它通常用于通用 return 类型,例如。

Future<Void> future = executorService.submit(new Callable<Void>() {
    public Void call() throws IOException {
        try(FileInputStream fis = new FileInputStream(filename)) {
            doSomething(fis);
        }
        return null;
    }
});

// later
future.get();

你可能想知道,为什么不使用 Runnable 因为我们不需要 return null;,但是 Runnable 不能抛出已检查的异常,所以如果我们希望捕获异常,我们会使用 Callable future 对象中的 IOException。

在 Java 8 中,您可以使用 lambda,但如果您抛出已检查的异常,编译器仍会期望 return 为 null,因为它必须使用 Callable

Future<Void> future = executorService.submit(() -> {
        try(FileInputStream fis = new FileInputStream(filename)) {
            doSomething(fis);
        }
        return null;
    });