Java Lambda表达式是否使用“隐藏”或本地包导入?

时间:2015-01-27 21:50:29

标签: 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();
        }
    }
}

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

所以我的问题是:

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

    < / LI>
  2. 如果使用lambda表达式执行“隐藏”下的导入,那么为什么当我创建一个接受实例的方法时,我需要添加import Path作为参数?

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

4 个答案:

答案 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>名称。因此,这也不需要导入OpenOptionjava.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即时创建新类型,因此您不需要导入,因为它是新类型(如运行时的泛型类型)