【问题标题】:Scala: trait extends java.nio.file.FileVisitorScala:特征扩展 java.nio.file.FileVisitor
【发布时间】:2015-06-03 23:00:45
【问题描述】:

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

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

我注意到 paulp 维护的 github 上的一个实现,我无法理解。我将在这里展示他的代码,以及我的问题和疑虑:

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 欢迎使用 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.Path

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

斯卡拉>


除此之外,我现在查看了 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;
    }  

}

在这一切之后,我提出以下意见: 类 Simple 扩展了 SimpleFileVisitor,它是 Java FileVisitor 接口的一个实现

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

这里让我感到困惑的是: 1)为什么他在扩展 SimpleFileVisitor 的同时还混合了 PathVisitor 特征? 2) 当它们是相同的方法时,我们不是试图要求 Simple 类同时尊重 SimpleVisitor 契约和 FileVisitor trait 未实现的方法吗? 3)他包装了Simple Class,一堆vals来表示SimpleFileVisitor方法的返回类型和一个apply方法。好的,那么您认为这种结构背后的想法是什么?

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

【问题讨论】:

  • 保罗这样做是“正确的方式”,我希望我有时间解释原因。 (正确的方法不止一种,但这是其中之一。)我希望其他人会这样做!关键是要考虑这些东西对你的面向 Scala 的 API 有什么作用。如果你自己想通了,请写一个答案!
  • 感谢您的洞察力。我一定会报告我的发现。这是一个令人兴奋的搜索
  • 我应该说--我假设这是正确的方法,因为它适合我没有的更大的图片。

标签: java scala nio traits


【解决方案1】:

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

然后他以 Scala 风格为您提供常量,因此您不必从 Java 中获取它们。

他只需混合PathVisitor trait 即可为您提供特定于路径的SimpleFileVisitor,而无需任何额外工作。

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

type PathVisitor = java.nio.file.FileVisitor

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

现在,针对具体问题:

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

  2. 要求尊重多个匹配的方法不是问题;同一个方法可以是多个特征的 API 的一部分。关键是你是否可以使用你想要的 trait(即 PathVisitor;答案是“是”)。

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

【讨论】:

  • 很好的解释。感谢您抽出宝贵的时间。 “有两个原因。首先,类型别名并不是真正的一流语言结构,所以你会倾向于看到 Java 类型而不是 Scala 类型(稍微不太好)。另外,如果你以这种方式设置它您以后可以更轻松地向框架中添加功能。如果您确定不想添加任何东西,那么这样做的争论就更少了。这是最好的部分。
  • “要求尊重多个匹配的方法不是问题;” - 太棒了。我不知道这不会是一个问题,甚至是理智的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-11
  • 1970-01-01
  • 1970-01-01
  • 2012-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多