24

In a Scala application, I am trying to read lines from a file using the Java NIO try-with-resources 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 errors like the following:

not found: value stream
A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.

I'm not sure what the problem is. I am new to using Java NIO, so any help is much appreciated.

8
  • 1
    There is nothing wrong in the java code Commented Oct 5, 2016 at 5:29
  • Is this applicable? Commented Oct 5, 2016 at 5:32
  • 6
    Scala and Java are different languages. You can't just expect Java syntax will compile fine in a Scala program. Related: stackoverflow.com/questions/25634455/…, stackoverflow.com/questions/2207425/… Commented Oct 5, 2016 at 5:33
  • Of course the Scala compiler will throw errors. That's not Scala code. Commented Oct 5, 2016 at 5:55
  • @jwvh ,thanks understood. Does scala have try-with-resources equivalent construct? Commented Oct 5, 2016 at 6:34

4 Answers 4

65

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. Call the first function to create the closable resource.
  2. Call the second function, and provide the resource as a parameter.
  3. Hold on to the returned value of the second function.
  4. Call close on the resource.
  5. Return the value (or exception) it got from the second function wrapped in a Try.

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.

Pre-Scala 2.13

In older versions of Scala, there is no directly support for javas try-with-resources construct, but your can pretty easily 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() } } 

This defines a reusable method that works pretty much like a try-with-resource construct in Java. It takes two parameters. First is an instance of AutoClosable, and second is a function that takes the same AutoClosable type as a paremeter and returns the same type as the method. The method then executes the function inside a try, and close the AutoClosable in its finally block.

You can use it like the following (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 you want to catch exceptions, you have 2 choices:

  1. Add a try/catch block around the stream.findAny() call.
  2. 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.

Note that as Vitalii Vitrenko points out in a comment, 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 throw an exception. Java's try-with-resources handles this, and we can make autoClose do so too, 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() } } } 

This works by storing the potential 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 the actual workings of try-with-resources: Better Resource Management with Java SE 7: Beyond Syntactic Sugar

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

This could then 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 could just call .get on myTry to make it return the result, or throw the exception.

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) 

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.

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.

Sign up to request clarification or add additional context in comments.

10 Comments

Be aware that unlike Java try-with-resources this solution doesn't handle a case when both fun(closeable) and close() throw exceptions. The last exception just silently hide the first one! You probably should close a resource inside another try statement and use addSuppressed() as demonstrated in this answer
The documentation for Using states that exceptions are suppressed: "If two exceptions are thrown (e.g., by an operation and closing a resource), one of them is re-thrown, and the other is added to it as a suppressed exception.". Am I missing something?
this comment was about autoClose() function and was posted before Using was introduced in 2.13 and the answer updated.
This is one reason I'm not a fan of people updating answers as new solutions are added. Using would have been better added as its own answer, rather than updating the existing one. I also didn't realize that there were two answers in here (the old and new); it looked like a wall of text explaining Using. I'll break up the text a little to make it clear in that regard.
Thanks @VitaliiVitrenko your are correct. I have updated the answer to include your input.
The implementation with Try is wrong. Try only catches NonFatal exceptions, but the resources must be cleaned even for fatal and control flow exceptions. Consider this code: class MyClass extends AutoCloseable { override def close() = println("Closing!") }; def foo: Try[Int] = { autoCloseTry(new MyClass) { _ => return Success(0) } }. Calling foo won't call close with autoCloseTry, but will work OK with autoClose.
Thanks Kolmar. I have tried to update the answer, to have a solution to that problem.
@smac89 I have added an example.
|
3

You can use Choppy's Lazy TryClose Monad to do this in a for-comprehension in a composeable manner similar to Scala's Try. (Disclaimer: I am the author.)

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 stream:

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

Comments

0

You have the already mentioned in one of the answers approach:

 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 last one IMHO it's easier to handle the control flow.

1 Comment

You don't handle the case where resource.close() throws an Exception? This answer does: stackoverflow.com/a/34277491/501113
0

Starting with Scala 2.13 the Using utility can be used, which works very similarly to Java's try-with-resources — including suppressing secondary exceptions when multiple occur.

Using(Files.lines(Paths.get("somefile.txt"))) { stream => stream.forEach(System.out.println(_)) // will do business process here } match { case Failure(e: IOException) => e.printStackTrace() // will handle failure case here } 

Scaladoc

Using.apply

def apply[R, A](resource: => R)(f: (R) => A)(implicit arg0: Releasable[R]): Try[A]

Performs an operation using a resource, and then releases the resource, even if the operation throws an exception.

[…]

returns a Try containing an exception if one or more were thrown, or the result of the operation if no exceptions were thrown

Using

object Using

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

Usage

There are multiple ways to automatically manage resources with Using. If you only need to manage a single resource, the apply method is easiest; it wraps the resource opening, operation, and resource releasing in a Try.

[…]

Suppression Behavior

If two exceptions are thrown (e.g., by an operation and closing a resource), one of them is re-thrown, and the other is added to it as a suppressed exception. If the two exceptions are of different 'severities' (see below), the one of a higher severity is re-thrown, and the one of a lower severity is added to it as a suppressed exception. If the two exceptions are of the same severity, the one thrown first is re-thrown, and the one thrown second is added to it as a suppressed exception. If an exception is a ControlThrowable, or if it does not support suppression (see Throwable's constructor with an enableSuppression parameter), an exception that would have been suppressed is instead discarded.

[…]

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.