Scala:特征扩展 java.nio.file.FileVisitor

Scala: trait extends java.nio.file.FileVisitor

我每天都在 Scala 中学习新东西。我目前采取的路线是从 java nio 中提取功能并从中提取 Scala 实现。我观察了其他 Scala 专家如何使用 java.nio.files 包和 FileVisitor 接口递归遍历包含子目录和文件的嵌套目录结构。

但是,我运行遇到了一个小问题。我不太明白

我注意到 paulp 在 github 上维护的一个实现,我不能 understand.It 是他的代码,我将在这里展示他的代码,以及我的问题和疑虑:

import java.nio.file.{ FileVisitResult, SimpleFileVisitor }

      trait PathVisitor extends FileVisitor[Path] {
         def preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult
         def postVisitDirectory(dir: Path, exc: IOException): FileVisitResult
        def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult
        def visitFileFailed(file: Path, exc: IOException): FileVisitResult
     }

好吧,他扩展自 FileVisitor,这是一个 Java 接口:起初,我不确定 Scala trait 是否可以从 java 接口扩展。我在 REPL 中对此进行了测试。显然这没问题。 REPL 输出如下:

C:\Users\lulu\Documents\GitHub\akkaexperiments>斯卡拉 欢迎使用 Scala 版本 2.10.2(Java HotSpot(TM) 64 位服务器 VM,Java 1.7.0_71)。 键入表达式以评估它们。 输入 :help 获取更多信息。

scala> 导入 java.nio.file.{ FileVisitor }; 导入 java.nio.file.FileVisitor

scala> 导入 java.nio.file.Path 导入 java.nio.file.路径

scala> trait PathVisitor extends FileVisitor[Path] 定义特征 PathVisitor

scala>


除此之外,我现在查看了 FileVisitor.java 的来源。如下所示:这就是 paulp 吸引人的地方。下面的代码后面有解释。

  public interface FileVisitor<T> {
        FileVisitResult preVisitDirectory(T dir);

        FileVisitResult preVisitDirectoryFailed(T dir, IOException exc);

         FileVisitResult visitFile(T file, BasicFileAttributes attrs);

         FileVisitResult visitFileFailed(T file, IOException exc);

         FileVisitResult postVisitDirectory(T dir, IOException exc);

      }

---------------------

paulp's code continues below:

    object PathVisitor {
      class Simple extends SimpleFileVisitor[Path] with PathVisitor { }

      val Continue     = FileVisitResult.CONTINUE
      val SkipSiblings = FileVisitResult.SKIP_SIBLINGS
      val SkipSubtree  = FileVisitResult.SKIP_SUBTREE
      val Terminate    = FileVisitResult.TERMINATE

      def apply(f: (Path, BasicFileAttributes) => FileVisitResult): PathVisitor = new Simple {
        override def visitFile(file: Path, attrs: BasicFileAttributes):  FileVisitResult = f(file, attrs)
      }

    }

------

For context and comparison purposes here is the code for SimpleFileVisitor:

     public class SimpleFileVisitor<T> implements FileVisitor<T> {

           protected SimpleFileVisitor() {
          }

          @Override
         public FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
        throws IOException
        {
           Objects.requireNonNull(dir);
           Objects.requireNonNull(attrs);
           return FileVisitResult.CONTINUE;
        }

         @Override
    public FileVisitResult visitFile(T file, BasicFileAttributes attrs)
        throws IOException
    {
        Objects.requireNonNull(file);
        Objects.requireNonNull(attrs);
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFileFailed(T file, IOException exc)
        throws IOException
    {
        Objects.requireNonNull(file);
        throw exc;
    }

   @Override
    public FileVisitResult postVisitDirectory(T dir, IOException exc)
        throws IOException
    {
        Objects.requireNonNull(dir);
        if (exc != null)
            throw exc;
        return FileVisitResult.CONTINUE;
    }  

}

经过这一切,我做出以下观察: class Simple 扩展了 SimpleFileVisitor,它是 Java FileVisitor 接口的一个实现

paulp还混入了trait PathVisitor,其方法定义与JavaFileVisitor接口中的方法定义完全相同。

这里让我莫名其妙的是: 1) 为什么他在扩展 SimpleFileVisitor 的同时还混合了 PathVisitor 特性? 2) 我们是否不尝试询问 class Simple 来尊重 SimpleVisitor 契约和 FileVisitor 特征未实现的方法,当它们是相同的方法时? 3) 他包装了 Simple Class,一堆表示 SimpleFileVisitor 方法的 return 类型的 val 和一个 apply 方法。好的,那么您认为这种结构背后的想法是什么?

我真的很想使用 PaulP 布局的结构,但这令人费解。它可能需要一些清理。 请指教

这样做的原因是为了提供无缝的 Scala 体验。首先,第一个特征指定路径,而不是抽象您可能有访问者的任何 T。如果您真的只对路径感兴趣,那么不必担心泛型是件好事。

然后他以 Scala 风格为您提供常量,因此您不必从 Java.

中获取它们

他给你一个路径特定的 SimpleFileVisitor,不需要任何额外的工作,只需混合 PathVisitor 特征。

现在,问题仍然存在:为什么要 这个 而不是只说

type PathVisitor = java.nio.file.FileVisitor

有两个原因。首先,类型别名并不是真正的 first-class 语言构造,因此您会倾向于看到 Java 类型而不是 Scala 类型(稍微不太好)。此外,如果您以这种方式设置它,您以后可以更轻松地将功能添加到框架中。如果您确定不想添加任何内容,那么这样做的理由就更少了。

现在,对于具体问题:

  1. 我们混合了 PathVisitor,所以我们有一个特定于 Scala 的类型,我们可以四处移动和操作;我们从 Java SimpleFileVisitor 中获取实现,但让它获得 Scala 特性。

  2. 要求承认多个匹配的方法不是问题;相同的方法可以是多个特征的 API 的一部分。关键是你能不能使用你想要的trait(即PathVisitor;答案是"yes")。

  3. 本地提供的 Scala 风格的常量有助于清理您的导入并提供更无缝的体验,就像 apply 风格的构建器而不是 new.