java try-with-resource无法与scala一起使用 [英] java try-with-resource not working with scala

查看:98
本文介绍了java try-with-resource无法与scala一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Scala应用程序中,我正在尝试使用java nio try-with-resource构造从文件中读取行.

In Scala application, am trying to read lines from a file using java nio try-with-resource construct.

Scala版本2.11.8
Java版本1.8

Scala version 2.11.8
Java version 1.8

try(Stream<String> stream = Files.lines(Paths.get("somefile.txt"))){
    stream.forEach(System.out::println); // will do business process here
}catch (IOException e) {
    e.printStackTrace(); // will handle failure case here
}  

但是编译器会抛出类似
的错误 ◾未找到:价值流
try没有成功的尝试或最终等于把自己的身体挡在一块;没有例外.

But the compiler throws error like
◾not found: value stream
◾A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.

不确定是什么问题. Java NIO的使用是新手,所以非常感谢您的帮助.

Not sure what is the problem. Am new to using Java NIO, so any help is much appreciated.

推荐答案

如果您使用的是Scala 2.13,则应使用

If your are on Scala 2.13 then you should use the Using object:

import scala.util.Using
val a: Try[Int] = Using(new FileInputStream("/tmp/someFile")) { fileInputStream =>
  // Do what you need in fith you fileInputStream here.
}

它具有两个功能.第一个功能是可以创建或提供可关闭资源的功能,第二个功能是将可关闭资源作为参数并将其用于某些用途的功能.然后,将使用简单的术语为您执行以下操作:

It takes two functions. The first one is a function that can create or provide the closable resource, and the second function is the one that takes the closable resource as a parameter, and can use it for something. Using will then in simple terms do the following for you:

  1. 调用第一个函数来创建可关闭的资源.
  2. 调用第二个函数,并提供资源作为参数.
  3. 保留第二个函数的返回值.
  4. 调用关闭资源.
  5. 返回从包装在Try中的第二个函数获得的值(或异常).

除了可以实现AutoCloseable的Classs以外,它还可以用于其他许多用途,您只需要提供一个隐式值即可,告诉Using如何关闭您的特定资源.

Using can be used on many other things than Classes that implements AutoCloseable, you just have to provide an implicit value, telling Using how to close your specific resource.

在旧版本的scala中,不直接支持javas try-with-resources构造,但是您可以通过应用贷款模式来轻松构建自己的支持.以下是一个简单但并非最佳的示例,易于理解.此答案稍后会给出更正确的解决方案:

In older versions of scala, there is no directly support for javas try-with-resources construct, but your can pretty easy build your own support, by applying the loan pattern. The following is a simple but not optimal example, that is easy to understand. A more correct solution is given later in this answer:

import java.lang.AutoCloseable

def autoClose[A <: AutoCloseable,B](
        closeable: A)(fun: (A) ⇒ B): B = {
    try {
        fun(closeable)
    } finally {
        closeable.close()
    }
}

这定义了一个可重用的方法,其工作原理非常类似于Java中的try-with-resource构造.它通过两个参数来工作.首先是采用Autoclosable实例的子类,其次是采用与参数表相同的Autoclosable类型的函数.函数参数的返回类型用作方法的返回类型.然后,该方法在try内执行该函数,并在其finally块中关闭autocloseble.

This defines a reusable method, that works pretty much like a try-with-resource construct in java. It works by taken two parameter. First is takes the a subclass of Autoclosable instance, and second it takes a function that takes the same Autoclosable type as a paremeter. The return type of the function parameter, is used as return type of the method. The method then executes the function inside a try, and close the autocloseble in its finally block.

您可以像这样使用它(此处用于获取流中findAny()的结果.

You can use it like this (here used to get the result of findAny() on the stream.

val result: Optional[String] = autoClose(Files.lines(Paths.get("somefile.txt"))) { stream ⇒
    stream.findAny()
}

如果您想捕获异常,则有两种选择.

In case your want to do catch exceptions, you have 2 choices.

  1. 在stream.findAny()调用周围添加一个try/catch块.

  1. Add a try/catch block around the stream.findAny() call.

或在autoClose方法中将catch块添加到try块.请注意,只有在catch块中的逻辑在调用autoClose的所有位置都可用的情况下,才应这样做.

Or add a catch block to the try block in the autoClose method. Note that this should only be done, if the logic inside the catch block is usable from all places where autoClose is called.

请注意,正如Vitalii Vitrenko指出的那样,如果客户端提供的函数和AutoCloseable上的close方法都抛出异常,则此方法将从close方法中吞下异常. Java的try-with-resources可以解决这个问题,我们可以通过使autoClose稍微复杂一些来做到这一点:

Note that as Vitalii Vitrenko point out, this method will swallow the exception from the close method, if both the function supplied by the client, and the close method on the AutoCloseable throws an exception. Javas try-with-resources handles this, and we can make autoClose do so to, by making it a bit more complex:

  def autoClose[A <: AutoCloseable,B](
      closeable: A)(fun: (A) ⇒ B): B = {

    var t: Throwable = null
    try {
      fun(closeable)
    } catch {
      case funT: Throwable ⇒
        t = funT
        throw t
    } finally {
      if (t != null) {
        try {
          closeable.close()
        } catch {
          case closeT: Throwable ⇒
            t.addSuppressed(closeT)
            throw t
        }
      } else {
        closeable.close()
      }
    }
  }

这可以通过存储客户端函数引发的潜在异常,并将close方法的潜在异常作为禁止的异常添加到该异常中来工作.这与oracle解释try-with-resource实际上是这样做的方式非常接近:

This works by storing the potentially exception the client function throws, and adding the potential exception of the close method to it as a supressed exception. This is pretty close to how oracle explains that try-with-resource is actually doing it: http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html

但是这是Scala,许多人会更喜欢以更实用的方式进行编程.以更实用的方式,该方法应返回Try,而不是引发异常.这避免了引发异常的副作用,并且使客户端清楚地知道响应可能是应该处理的失败(如Stas的答案所指出).在功能实现中,我们还希望避免使用var,因此可以尝试进行天真的尝试:

However this is Scala, and a lot of people will prefer to program in a more functional way. In a more functional way, the method should return a Try, instead of throwing an exception. This avoids a side effect of throwing an exception, and makes it clear to the client that the response may be a failure that should be handled (as pointed out in the answer by Stas). In a functional implementation, we would also like to avoid having a var, so a naive attempt could be:

  // Warning this implementation is not 100% safe, see below
  def autoCloseTry[A <: AutoCloseable,B](
      closeable: A)(fun: (A) ⇒ B): Try[B] = {

    Try(fun(closeable)).transform(
      result ⇒ {
        closeable.close()
        Success(result)
      },
      funT ⇒ {
        Try(closeable.close()).transform(
          _ ⇒ Failure(funT),
          closeT ⇒ {
            funT.addSuppressed(closeT)
            Failure(funT)
          }
        )
      }
    )
  }

可以这样称呼他们吗:

    val myTry = autoCloseTry(closeable) { resource ⇒
      //doSomethingWithTheResource
      33
    }
    myTry match {
      case Success(result) ⇒ doSomethingWithTheResult(result)
      case Failure(t) ⇒ handleMyExceptions(t)
    }

或者您可以在myTry上调用.get使其返回结果,或引发异常.

Or you could just call .get on myTry to make it return the result, or throw the exception.

但是,正如Kolmar在评论中指出的那样,由于return语句在scala中的工作方式,此实现存在缺陷.请考虑以下内容:

However as Kolmar points out in a comment, this implementation is flawed, due to how the return statement works in scala. Consider the following:

  class MyClass extends AutoCloseable {
    override def close(): Unit = println("Closing!")
  }

  def foo: Try[Int] = {
     autoCloseTry(new MyClass) { _ => return Success(0) }
  }

  println(foo)

我们希望它会显示Closing !,但不会.这里的问题是函数体内的显式return语句.它使该方法跳过autoCloseTry方法中的逻辑,从而仅返回Success(0),而不关闭资源.

We would expect this to print Closing!, but it will not. The problem here is the explicit return statement inside the function body. It makes the method skip the logic in the autoCloseTry method, and thereby just returns Success(0), without closing the resource.

要解决该问题,我们可以创建两种解决方案的组合,一种具有返回Try的功能API,但使用基于try/finally块的经典实现:

To fix that problem, we can create a mix of the 2 solutions, one that has the functional API of returning a Try, but uses the classic implementation based on try/finally blocks:

    def autoCloseTry[A <: AutoCloseable,B](
        closeable: A)(fun: (A) ⇒ B): Try[B] = {

      var t: Throwable = null
      try {
        Success(fun(closeable))
      } catch {
        case funT: Throwable ⇒
          t = funT
          Failure(t)
      } finally {
        if (t != null) {
          try {
            closeable.close()
          } catch {
            case closeT: Throwable ⇒
              t.addSuppressed(closeT)
              Failure(t)
          }
        } else {
          closeable.close()
        }
      }
    }

这应该可以解决问题,并且可以像第一次尝试一样使用.但是,它表明这有点容易出错,并且在相当长的一段时间里,错误的实现都被作为推荐的版本存在于此答案中.因此,除非您试图避免使用多个库,否则应适当考虑使用库中的此功能.我认为已经有另一个答案指向一个答案,但是我的猜测是存在多个库,以不同的方式解决了这个问题.

This should fix the problem, and can be used just like the first attempt. However it shows that this a bit error prone, and the faulty implementation has been in this answer as the recommended version for quite some time. So unless you trying to avoid having to many libraries, you should properly consider using this functionality from a library. I think that there is already one other answer pointing to one, but my guess is that there is multiply libraries, that solves this problem in different ways.

这篇关于java try-with-resource无法与scala一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆