Я каждый день изучаю новые вещи в Scala. Текущий маршрут, который я принимаю, состоит в том, чтобы вытащить функциональность из java nio и сделать из них реализацию Scala. Я наблюдал, как другие эксперты Scala используют пакет java.nio.files и интерфейс FileVisitor для рекурсивного перехода по структуре вложенных каталогов с подкаталогами и файлами.
Но у меня возникла небольшая проблема. Я не могу понять
Я заметил реализацию на github, поддерживаемую paulp, что я не могу понять. Это его код, который я приведу здесь, с моими вопросами и проблемами:
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 может простираться от java-интерфейса. Я проверил это в REPL. По-видимому, это хорошо. Выход REPL приведен ниже:
C:\Users\lulu\Documents\GitHub\akkaexperiments> scala Добро пожаловать в версию Scala версии 2.10.2 (Java-сервер HotSpot TM TM, Java 1.7.0_71). Введите выражения, чтобы они были оценены. Тип: помощь для получения дополнительной информации.
scala> import java.nio.file. {FileVisitor}; import java.nio.file.FileVisitor
scala> import java.nio.file.Path import java.nio.file.Path
scala> trav PathVisitor расширяет свойство FileVisitor [Path] PathVisitor
Scala>
С этой точки зрения я теперь посмотрел на источник FileVisitor.java. Вот он внизу: здесь интригует папу. Пояснение следует после приведенного ниже кода.
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 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 extends SimpleFileVisitor, который представляет собой реализацию интерфейса Java FileVisitor
Также paulp смешивает в trait PathVisitor, определения метода которого точно такие же, как и в интерфейсе Java FileVisitor.
Что меня озадачивает здесь: 1) почему он расширил SimpleFileVisitor в то же время, когда он также смешивается с признаком PathVisitor? 2) Разве мы не пытаемся просить класс Simple соблюдать как контракт SimpleVisitor, так и метод FileVisitor не реализовывать, когда они являются одними и теми же методами? 3) Он обертывает простой класс, кучу валов, чтобы представить возвращаемые типы методов SimpleFileVisitor и метод apply. Хорошо, так что, по-вашему, идея такой структуры?
Я бы очень хотел использовать структуру, изложенную PaulP, но это озадачивает. Возможно, это требует некоторой очистки. Пожалуйста, порекомендуйте.
Причина этого - обеспечить бесшовный опыт Scala. Во- первых, вместо того, чтобы абстрагировать над какой T
вы можете иметь посетителя для, первый признак определяет пути. Если вас действительно интересуют только пути, приятно не беспокоиться о дженериках.
Затем он предоставляет вам константы в стиле Scala, поэтому вам не нужно их захватывать с Java.
И он дает вам SimpleFileVisitor
специфичный для SimpleFileVisitor
без какой-либо дополнительной работы, просто смешивая в PathVisitor
.
Теперь остается вопрос: зачем это делать, а не просто говорить
type PathVisitor = java.nio.file.FileVisitor
Есть две причины. Во-первых, типы псевдонимов не являются действительно первоклассными языковыми конструкциями, поэтому вы, скорее всего, будете видеть тип Java вместо Scala (немного менее приятный). Кроме того, если вы установите его таким образом, вы сможете более легко добавить функциональность в структуру позже. Если вы уверены, что не захотите что-либо добавить, тем меньше аргументов для этого.
Теперь, по конкретным вопросам:
Мы смешиваем в PathVisitor, поэтому у нас есть Scala-специфический тип, который мы можем перемещать и манипулировать; мы получаем реализации Java SimpleFileVisitor
но заставляем его забирать черту Scala.
Просить соблюдать несколько методов, которые соответствуют, не является проблемой; тот же метод может быть частью API нескольких признаков. Ключ заключается в том, можете ли вы использовать желаемый признак (т.е. PathVisitor, ответ "да").
Константы Scala -style, предоставляемые локально, помогают очистить ваш импорт и обеспечивают более бесшовный опыт, а также apply
построитель -style вместо new
.