Java try-with-resource does not work with scala

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

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 } 

But the compiler throws an error, for example, โ—พ not found: value stream
โ—พ Try without a catch or, finally, equivalent to placing his body in a block; no exceptions are handled.

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

+8
source share
4 answers

If you are using Scala 2.13, you should use the " " object .

In older versions of scala, there is no direct support for the javas try-with-resources construct, but you can pretty easily create your own support by applying a loan template. The following is a simple but not optimal example that is easy to understand. A more correct solution will be 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() } } 

This defines a reusable method that works almost like a try-with-resource construct in java. It works on two parameters. The first is a subclass of the Autoclosable instance, and the second is a function that takes the same Autoclosable type as the parameter. The return type of the function parameter is used as the return type of the method. The method then executes the function inside try and closes autocomplete in its finally block.

You can use it as follows (here it is used to get the result of findAny () in the stream.

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

In case you want to make a catch exception, you have 2 options.

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

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

Please note that, as Vitaliy Vitrenko points out, this method will swallow an exception from the close method if both the function provided by the client and the close method in AutoCloseable throw an exception. Javas try-with-resources handles this, and we can get autoClose to do this, making it a little more complicated:

  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() } } } 

This works by preserving the potential exception that the client function throws and adding the potential close exception to it as a suppressed exception. This is pretty close to how the oracle explains that try-with-resource actually does this: http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html

However, this is Scala, and many people prefer to program more functionally. In a more functional way, the method should return Try, rather than throw an exception. This avoids the side effect of throwing an exception and makes it clear to the client that the answer may be a mistake that should be handled (as indicated in Stas's answer). In a functional implementation, we would also like to avoid using the var variable, so a naive attempt might 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) } ) } ) } 

It could be called like this:

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

Or you can just call .get on myTry to return a result, or throw an exception.

However, as Colmar noted in the commentary, this implementation has drawbacks due to the way 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) 

We expect this to print Closing!, But it is not. The problem here is the explicit expression of the return within the body of the function. It forces the method to skip the logic in the autoCloseTry method and thus simply returns Success (0) without closing the resource.

To solve this problem, we can create a combination of 2 solutions, one of which has a functional API to return 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 solve the problem and can be used as on the first try. However, this shows that this is slightly error prone, and the erroneous implementation has been the recommended version in this answer for quite some time. Therefore, if you are not trying to avoid using a large number of libraries, you should consider using this function from the library. I think that there is already one other answer pointing to one, but I assume that there are several libraries that solve this problem in different ways.

+24
source

Alternatively, you can use Choppy TryClose monad for this in an understandable way similar to Scala Try.

 val ds = new JdbcDataSource() val output = for { conn <- TryClose(ds.getConnection()) ps <- TryClose(conn.prepareStatement("select * from MyTable")) rs <- TryClose.wrap(ps.executeQuery()) } yield wrap(extractResult(rs)) 

Here's how you would do it with your thread:

 val output = for { stream <- TryClose(Files.lines(Paths.get("somefile.txt"))) } yield wrap(stream.findAny()) 

More details here: https://github.com/choppythelumberjack/tryclose

+1
source

You already mentioned in one of the answers:

  def autoClose[A <: AutoCloseable, B](resource: A)(code: A โ‡’ B): B = { try code(resource) finally resource.close() } 

But I think the following is much more elegant:

  def autoClose[A <: AutoCloseable, B](resource: A)(code: A โ‡’ B): Try[B] = { val tryResult = Try {code(resource)} resource.close() tryResult } 

With the latest IMHO, itโ€™s easier to handle the control flow.

0
source

At the moment, Scala 2.13 is finally supported: try with resources using Using :), Example:

 val lines: Try[Seq[String]] = Using(new BufferedReader(new FileReader("file.txt"))) { reader => Iterator.unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList } 

Utility for automatic resource management. It can be used to perform an operation using resources, after which it releases resources in the reverse order of their creation.

0
source

Source: https://habr.com/ru/post/1257714/


All Articles