这个问题是关于lambda表达式似乎采用的Java包的明显“隐藏”或本地导入。
以下示例代码编译并运行正常(它只列出给定目录中的文件):
package com.mbm.stockbot;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Temp2 {
public static void main(String[] args) {
Temp2 t = new Temp2();
t.readDir();
}
public void readDir() {
try {
Files.walk(Paths.get("C:/Users/mbmas_000/Downloads/SEC Edgar"), 1).forEach(filePath -> {
if (Files.isRegularFile(filePath)) {
System.out.println(filePath);
}
});
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
请注意,变量filePath
是Path
的一个实例,我认为其实现包含在包java.nio.file.Path
中,尽管该包没有import
。< / p>
现在,如果我进行一些小修改,请将对System.out.println
的调用重构为自己的方法:
package com.mbm.stockbot;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Temp2 {
public static void main(String[] args) {
Temp2 t = new Temp2();
t.readDir();
}
public void readDir() {
try {
Files.walk(Paths.get("C:/Users/mbmas_000/Downloads/SEC Edgar"), 1).forEach(filePath -> {
if (Files.isRegularFile(filePath)) {
printPath(filePath);
}
});
} catch (IOException e1) {
e1.printStackTrace();
}
}
public void printPath(Path passedFilePath) {
System.out.println(passedFilePath);
}
}
我现在必须'导入'import java.nio.file.Path
,否则我会收到编译错误。
所以我的问题是:
如果filePath
确实是java.nio.file.Path
的实例,为什么不我需要在第一个示例中导入它,
如果使用lambda表达式执行“隐藏”下的导入,那么为什么做当我创建一个接受实例的方法时,我需要添加import
Path
作为参数?
可用于调用filePath
和passedFilePath
的方法完全相同,这让我相信它们都是java.nio.file.Path
的实例。
答案 0 :(得分:6)
import
声明并不意味着声明您的代码正在使用哪些类;他们只是声明用于解析非限定标识符的内容。因此,如果您在代码中使用非限定标识符Path
,则必须使用import java.nio.file.Path;
来声明它应该被解析为此限定类型。顺便说一下,这不是解决名称的唯一方法。名称也可以通过类继承来解决,例如如果它们与继承的成员类的简单名称匹配。
如果您在不引用其名称的情况下隐式使用类型,则不需要import
语句,这不仅限于lambda表达式,它甚至不是特殊的Java 8功能。例如。与
Files.walk(Paths.get("C:/Users/mbmas_000/Downloads/SEC Edgar"), 1)
您已经隐式使用Path
类型,因为它是Paths.get
的返回类型和参数类型Files.walk
,换句话说,您正在接收{{1}的实例并将其传递给另一个方法而不引用其类型名称,因此您不需要java.nio.file.Path
。此外,您调用varargs方法接受任意数量的import
个实例。您没有指定任何内容,因此您的代码将创建一个零长度FileVisitOption
数组,并再次将其传递给FileVisitOption[]
,而不是Files.walk
。
使用改进的类型推断,还有另一种可能使用类型而不引用其名称,例如如果你打电话:
import
您不仅要为varargs参数创建零长度Files.newByteChannel(path, new HashSet<>());
数组而不按名称引用此类型,还要创建FileAttribute[]
而不引用类型HashSet<OpenOption>
名称。因此,这也不需要导入OpenOption
或java.nio.file.attribute.FileAttribute
。
所以最重要的是,你是否需要一个java.nio.file.OpenOption
不依赖于类型的使用,而是你是否通过它的简单名称来引用它(并且有多种方法可以使用没有用名字来指代它。在第二个示例中,您指的是方法import
中的名称Path
;如果您将其更改为printPath(Path passedFilePath)
,那么在没有明确printPath(Object passedFilePath)
import
的情况下,所有内容都会再次生效。
答案 1 :(得分:2)
不同之处在于,在第二个示例中,您声明了一个局部变量Path passedFilePath
(作为方法参数)。执行此操作时,需要import
告诉java编译器您指的是哪种类型Path
,因为多个包可以具有相同名称的类。您可能已经注意到,当您创建变量List something
并要求IDE自动创建导入时,大多数IDE通常会询问您是java.util.List
还是java.awt.List
。您还可以创建一个自己的类com.myorg.myproject.List
,这将是第三个选项。
在第一个示例中,filePath
的确切类型由Paths.get(...).forEach
所需的类型决定,因此您不需要告诉java编译器您引用的class Path
到。
顺便说一下,当您将方法签名重写为public void printPath(java.nio.file.Path passedFilePath)
时,可以省略第二个示例中的导入。提供完全限定的类名时,您不再需要导入,因为类名不能含糊不清。
你可能想知道&#34;但是当我在整个标准库中只有一个名为Path
的类并且我没有自己的类时,为什么我需要一个导入或完全限定的名称?那个名字?&#34; - 请记住,Java是为代码重用性而设计的。当您的代码在另一个项目中使用时,该项目可能有这样的类,或者可能使用第三方库,然后您的代码就会模糊不清。
答案 2 :(得分:0)
您需要在第二个示例中使用导入,因为您声明了一个变量。
这与lambda表达式没有任何关系。如果您使用匿名类,则会发生完全相同的事情。
答案 3 :(得分:0)
我认为您试图说明的观点可以简化为:
此lambda 需要导入
Paths.get("path").forEach((Path filePath) -> {});
此lambda 不需要导入
Paths.get("path").forEach((filePath) -> {});
由于Path.forEach(...)
需要Consumer<? super Path>
,我认为后一种情况是? super Path
即时创建新类型,因此您不需要导入,因为它是新类型(如运行时的泛型类型)