Как преобразовать поток Java в поток Scala?

В рамках усилий по преобразованию кода Java в код Scala мне необходимо преобразовать поток Java Files.walk(Paths.get(ROOT)) в Scala. Я не могу найти решение, прибегая к помощи. asScala не будет этого делать. Есть намеки?

Ниже приведен соответствующий код:

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Collectors;

....
   Files.walk(Paths.get(ROOT))
            .filter(path -> !path.equals(Paths.get(ROOT)))
            .map(path -> Paths.get(ROOT).relativize(path))
            .map(path -> linkTo(methodOn(FileUploadController.class).getFile(path.toString())).withRel(path.toString()))
            .collect(Collectors.toList()))

где Files.walk(Paths.get(ROOT)) - Stream<Path> в Java.

Ответ 1

Существует несколько более приятный способ, не требующий совместимого слоя или экспериментальных функций 2.11, упомянутых здесь @marcospereira

В основном просто используйте итератор:

import java.nio.file.{Files, Paths}
import scala.collection.JavaConverters._

Files.list(Paths.get(".")).iterator().asScala

Ответ 2

Потоки Java 8 Streams и Scala - концептуально разные вещи; Java 8 Stream не является коллекцией, поэтому обычный конвертер коллекции не будет работать. Вы можете использовать библиотеку scala-java8-compat (github) для добавления метода toScala в потоки Java:

import scala.compat.java8.StreamConverters._
import java.nio.file.{ Files, Path, Paths }

val scalaStream: Stream[Path] = Files.walk(Paths.get(".")).toScala[Stream]

Вы не можете использовать это преобразование (Java-> Scala) из Java, поэтому, если вам нужно сделать это с Java, проще (но все же неудобно) просто запустить поток и самостоятельно построить Scala Stream (что и есть вышеупомянутая библиотека делает под капотом):

import scala.collection.immutable.Stream$;
import scala.collection.mutable.Builder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

final Stream<Path> stream = Files.walk(Paths.get("."));
final Builder<Path, scala.collection.immutable.Stream<Path>> builder = Stream$.MODULE$.newBuilder();
stream.forEachOrdered(builder::$plus$eq);
final scala.collection.immutable.Stream<Path> result = builder.result();

Однако оба способа полностью поглотят поток Java, поэтому вы не получите выгоды от ленивой оценки, преобразовывая ее в поток Scala и можете просто преобразовать ее непосредственно в вектор. Если вы просто хотите использовать синтаксис литерала функции Scala, существуют разные способы достижения этого. Вы можете использовать одну и ту же библиотеку для использования преобразователей функций, подобных конверторам коллекции:

import scala.compat.java8.FunctionConverters._
import java.nio.file.{ Files, Path, Paths }

val p: Path => Boolean = p => Files.isExecutable(p)
val stream: java.util.stream.Stream[Path] = Files.walk(Paths.get(".")).filter(p.asJava)

Альтернативно, начиная с 2.11, Scala имеет экспериментальную поддержку типов SAM под флагом -Xexperimental. Это будет не экспериментально без флага в 2.12.

$ scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_92).
Type in expressions for evaluation. Or try :help.

scala> import java.nio.file.{ Files, Path, Paths }
import java.nio.file.{Files, Path, Paths}

scala> Files.walk(Paths.get(".")).filter(p => Files.isExecutable(p))
<console>:13: error: missing parameter type
       Files.walk(Paths.get(".")).filter(p => Files.isExecutable(p))
                                         ^

scala> :set -Xexperimental

scala> Files.walk(Paths.get(".")).filter(p => Files.isExecutable(p))
res1: java.util.stream.Stream[java.nio.file.Path] = [email protected]

scala> Files.walk(Paths.get(".")).filter(Files.isExecutable)
res2: java.util.stream.Stream[java.nio.file.Path] = [email protected]

Ответ 3

Начиная с Scala 2.13, стандартная библиотека включает scala.jdk.StreamConverters которая обеспечивает неявное преобразование потоков из Java в Scala:

import scala.jdk.StreamConverters._

val javaStream = Files.walk(Paths.get("."))
// javaStream: java.util.stream.Stream[java.nio.file.Path] = [email protected]
javaStream.toScala(LazyList)
// scala.collection.immutable.LazyList[java.nio.file.Path] = LazyList(?)
javaStream.toScala(Iterator)
// Iterator[java.nio.file.Path] = <iterator>

Обратите внимание на использование LazyList (в отличие от Stream), поскольку Stream был переименован в LazyList в Scala 2.13.