Java Lambda 表达式使用 "Hidden" 还是本地包导入?

Do Java Lambda Expressions Utilize "Hidden" or Local Package Imports?

这个问题是关于 lambda 表达式似乎使用的明显 "hidden" 或 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();
        }
    }
}

注意变量filePathPath的一个实例,我认为它的实现包含在包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,否则会出现编译错误。

所以我的问题是:

  1. 如果filePath确实是java.nio.file.Path的一个实例,为什么不要我需要在第一个例子中导入它, 和

  2. 如果使用 lambda 表达式执行导入 "under the covers," 那么为什么 do 我需要在创建时添加 importPath 的实例作为参数的方法?

可用于调用 filePathpassedFilePath 的方法是相同的,这让我相信它们都是 java.nio.file.Path.

的实例

不同之处在于,在第二个示例中,您声明了一个局部变量Path passedFilePath(作为方法参数)。当你这样做时,你需要一个 import 来告诉 java 编译器你指的是哪种类型 Path,因为多个包可以有一个同名的 class。您可能已经注意到,当您创建一个变量 List something 并要求 IDE 自动创建导入时,大多数 IDE 通常会问您您的意思是 java.util.List 还是java.awt.List。您还可以创建自己的 class com.myorg.myproject.List,这将是第三个选项。

在第一个示例中,filePath 的确切类型由 Paths.get(...).forEach 所需的类型确定,因此您无需告诉 java 编译器哪个 [=19] =]你指的是。

顺便说一句,您可以省略第二个示例中的导入,届时您会将方法签名重写为 public void printPath(java.nio.file.Path passedFilePath)。当提供完全限定的 class 名称时,您不再需要导入,因为 class-名称不能有歧义。

您可能想知道 "but why do I need an import or fully qualified name when there is only one class named Path in the whole standard library and I don't have an own class of that name?" - 请记住 Java 是为代码的可重用性而设计的。当您的代码在另一个项目中使用时,该项目可能有这样的 class 或者可能使用具有这样的第 3 方库,然后您的代码就会变得模棱两可。

您需要在第二个示例中使用导入,因为您要声明一个变量。

这与 lambda 表达式没有任何关系。如果您使用匿名 class,您会发生完全相同的事情。

我认为你试图说明的观点可以这样简化:

此 lambda 需要 导入

Paths.get("path").forEach((Path filePath) -> {});

此 lambda 不需要 导入

Paths.get("path").forEach((filePath) -> {});

由于 Path.forEach(...) 采用 Consumer<? super Path> 我认为后一种情况正在创建一个新类型 ? super Path,因此您不需要导入,因为它是一种新类型(就像运行时的​​泛型类型)

import 声明并不意味着声明您的代码使用的 classes;他们只是声明使用什么来解析不合格的标识符。因此,如果您在代码中使用非限定标识符 Path,则必须使用 import java.nio.file.Path; 来声明它应该解析为该限定类型。顺便说一句,这不是解析名称的唯一方法。名称也可以通过 class 继承来解析,例如如果它们匹配继承成员的简单名称 class.

如果您隐式使用类型而不引用其名称,则不需要 import 语句,这不仅限于 lambda 表达式,它甚至不是特殊的 Java 8 功能.例如。与

Files.walk(Paths.get("C:/Users/mbmas_000/Downloads/SEC Edgar"), 1)

您已经在隐式使用 Path 类型,因为它是 Paths.get 的 return 类型和 Files.walk 的参数类型,换句话说,您正在接收java.nio.file.Path 的实例并将其传递给另一个方法而不引用其类型名称,因此您不需要 import。此外,您正在调用接受任意数量 FileVisitOption 实例的可变参数方法。您没有指定任何内容,因此您的代码将创建一个零长度 FileVisitOption[] 数组并将其传递给 Files.walk,同样没有 import.

通过改进的类型推断,还有另一种可能性可以在不引用其名称的情况下使用类型,例如如果你打电话:

Files.newByteChannel(path, new HashSet<>());

您不仅为 varargs 参数创建了一个零长度 FileAttribute[] 数组而没有按名称引用此类型,您还创建了一个 HashSet<OpenOption> 而没有引用类型 OpenOption 按名字。所以这也不需要导入 java.nio.file.attribute.FileAttributejava.nio.file.OpenOption.


所以最重要的是,你是否需要一个 import 并不取决于类型的使用,而是取决于你是否通过它的简单名称来引用它(并且有不止一种使用方法键入而不按名称引用它)。在您的第二个示例中,您在方法 printPath(Path passedFilePath) 中引用了名称 Path;如果您将其更改为 printPath(Object passedFilePath),则无需 java.nio.file.Path.

的显式 import,一切都将再次运行