Как преобразовать поток 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
.