使用Try处理Scala中的Java NIO Iterator

时间:2014-08-06 19:33:06

标签: scala

我最近学会了如何使用Scala的原生Try类型来处理错误。 Try的一个好处是,我能够使用for -reherehence并默默地忽略错误。

然而,这对Java的NIO包(我真的想要使用)来说只是一个小问题。

val p = Paths.get("Some File Path")
for {
  stream <- Try(Files.newDirectoryStream(p))
  file:Path <- stream.iterator()
} yield file.getFileName

这本来是完美的。我打算从目录中获取所有文件名,使用DirectoryStream[Path]是最好的方法,因为它可以很好地扩展。 NIO页面说DirectoryStream有一个返回迭代器的iterator()方法。对于Java的for循环,它已经足够了,可以这样使用:

try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
    for (Path file: stream) {
        System.out.println(file.getFileName());
    }
}

然而,Scala不接受这一点。我受到了错误的欢迎:

[error] /.../DAL.scala:42: value filter is not a member of java.util.Iterator[java.nio.file.Path]
[error] file:Path <- stream.iterator

我尝试使用JavaConverters,它显示它处理Java Iterator类型:scala.collection.Iterator <=> java.util.Iterator,但当我尝试以这种方式调用它时:stream.iterator().asScala,方法无法访问。

我该怎么办?如何在使用NIO包的同时编写好的Scala代码?

2 个答案:

答案 0 :(得分:2)

我实际上并没有完全理解filter正在被调用,但请注意stream.iterator()会返回Iterator[Path],而不是Path,即使我的IDE认为它确实如此,可能是因为他认为他可以将map应用于它,但实际上这是在编译器确认的java.util.Iterator[java.nio.file.Path]上没有定义的方法:

scala>   for {
 |     stream <- Try(Files.newDirectoryStream(p))
 |     file <- stream.iterator()
 |   } yield file
<console>:13: error: value map is not a member of java.util.Iterator[java.nio.file.Path]
              file <- stream.iterator()

理解能力转化为:

Try(Files.newDirectoryStream(p)).flatMap(stream => stream.iterator().map(...))

未定义第二个map的位置。一个解决方案可以在这个SO question中找到,但我不能告诉你如何在这里使用迭代器进行理解,因为在java迭代器中无法映射,我不确定你是否可以将它转换为理解。

编辑:

我设法找到了更多关于这个问题的信息,我试着理解这个问题:

for {
  stream <- Try(Files.newDirectoryStream(p))
  file <- stream.iterator().toIterator
} yield file

这不能编译,因为:

found   : Iterator[java.nio.file.Path]
required: scala.util.Try[?]
       file <- stream.iterator().toIterator

它转换为:

Try(Files.newDirectoryStream(p)).flatMap(stream => stream.iterator().map(...))

但是flatMap实际上期望Try回来,实际上这有效:

Try(Files.newDirectoryStream(p)).flatMap(stream => Try(stream.iterator().map(...)))
                                                    ^

我想出了什么:

import java.nio.file.{Paths, Files}
import util.Try
import scala.collection.JavaConversions._

Try(Files.newDirectoryStream(p))
  .map(stream => 
    stream
    .iterator()
    .toIterator
    .toList
    .map(path => path.getFileName.toString)
).getOrElse(List())

返回一个List[String],不幸的是,这远不像你的理解那样漂亮,也许其他人有更好的主意。

答案 1 :(得分:0)

我非常喜欢Ende Neu所写的内容,并且很难在Scala中使用NIO。我想保留Java的Stream带来的效率,所以我决定改写这个函数。它仍然使用Try,我只需处理SuccessFailure个案例:)

它并不像我希望的那么顺利,如果没有Java 7的优秀try-with-resource功能,我必须自己关闭流(这很糟糕......),但这样做了。

  def readFileNames(filePath: String):Option[List[Path]] = {
    val p = Paths.get(filePath)
    val stream: Try[DirectoryStream[Path]] = Try(Files.newDirectoryStream(p))

    val listOfFiles = List[Path]()
    stream match {
      case Success(st) =>
        val iterator = st.iterator()
        while (iterator.hasNext) {
          listOfFiles :+ iterator.next()
        }
      case Failure(ex) => println(s"The file path is incorrect: ${ex.getMessage}")
    }
    stream.map(ds => ds.close())
    if(listOfFiles.isEmpty) None else Some(listOfFiles)
  }