Java Lambda表达式是否使用“隐藏”或本地包导入?
这个问题是关于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
。
现在,如果我进行一些小修改,请将对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’ import java.nio.file.Path
,否则我会收到编译器错误。
所以我的问题是:
-
如果
filePath
确实是java.nio.file.Path
一个实例,为什么我不需要在第一个例子中导入它, -
如果使用lambda表达式执行“隐藏”下的导入,那么当我创建一个以
Path
实例作为参数的方法时,为什么还需要添加import
?
可用于调用filePath
和passedFilePath
是相同的,这使我相信它们都是java.nio.file.Path
实例。
import
声明并不意味着声明你的代码正在使用哪些类; 他们只是声明用于解析非限定标识符的内容。 因此,如果您在代码中使用非限定标识符Path
,则必须使用import java.nio.file.Path;
声明它应该被解析为这种限定类型。 顺便说一下,这不是解决名称的唯一方法。 名称也可以通过类inheritance来解析,例如,如果它们与inheritance的成员类的简单名称匹配。
如果您在不引用其名称的情况下隐式使用类型,则不需要import
语句,这不仅限于lambda表达式,它甚至不是特殊的Java 8function。 例如
Files.walk(Paths.get("C:/Users/mbmas_000/Downloads/SEC Edgar"), 1)
您已经隐式使用Path
类型,因为它是Paths.get
的返回类型和Paths.get
的参数类型,换句话说,您正在接收java.nio.file.Path
的实例并将其传递给另一个方法而没有引用其类型名称,因此您不需要import
。 此外,您调用varargs方法接受任意数量的FileVisitOption
实例。 您没有指定任何内容,因此您的代码将创建一个零长度的FileVisitOption[]
数组,并再次将其传递给Files.walk
,而无需import
。
使用改进的类型推断,还有另一种可能使用类型而不引用其名称,例如,如果您调用:
Files.newByteChannel(path, new HashSet<>());
您不仅要为varargs参数创建零长度的FileAttribute[]
数组,而不是按名称引用此类型,还要创建HashSet
而OpenOption
按名称引用OpenOption
类型。 因此,这也不需要导入java.nio.file.attribute.FileAttribute
和java.nio.file.OpenOption
。
因此,最重要的是,您是否需要import
不依赖于类型的使用,而是您是否通过其简单名称引用它(并且有多种方法可以使用类型而不通过名称引用它)。 在第二个示例中,您指的是方法printPath(Path passedFilePath)
名称Path
printPath(Path passedFilePath)
; 如果将其更改为printPath(Object passedFilePath)
,则所有内容都将重新运行,而无需显式import
java.nio.file.Path
。
不同之处在于,在第二个示例中,您声明了一个局部变量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)
时,可以省略第二个示例中的导入。 提供完全限定的类名时,不再需要导入,因为类名不能含糊不清。
您可能想知道“但是,当整个标准库中只有一个名为Path
类并且我没有自己的该类名称时,为什么我需要导入或完全限定名称?” – 请记住,Java是为代码重用性而设计的。 当您的代码在另一个项目中使用时,该项目可能具有这样的类,或者可能使用第三方库,然后您的代码将是不明确的。
您需要在第二个示例中使用导入,因为您声明了一个变量。
这与lambda表达式没有任何关系。 如果您使用匿名类,则会发生完全相同的事情。
我认为您试图说明的观点可以简化为:
这个lambda 需要导入
Paths.get("path").forEach((Path filePath) -> {});
这个lambda 不需要导入
Paths.get("path").forEach((filePath) -> {});
因为Path.forEach(...)
需要Consumer super Path>
Consumer super Path>
我认为后一种情况是即时创建一种新类型? super Path
? super Path
,因此您不需要导入,因为它是一个新类型(如运行时的generics类型)