37

Suppose I have 3 Scanner instances which I want to close.

I could do

sc.close()

for each of the Scanners.

Or I could do something like

for (Scanner sc: new Scanner[]{sc1,sc2,sc3}) {
    sc.close();
}

Is there any shorter way of doing this with Java 8?

Something similar to?

{sc1,sc2,sc3}.forEach((sc) -> sc.close());
E. Sundin
  • 4,103
  • 21
  • 30
  • 1
    http://stackoverflow.com/questions/24390463/java-8-stream-and-operation-on-arrays – Tschallacka May 04 '16 at 09:24
  • 10
    To answer the general question: `Stream.of(sc1, sc2, sc3).forEach(Scanner::close);`, but for this specific case [listen to Gerald Mücke](http://stackoverflow.com/a/37023624/2711488) – Holger May 04 '16 at 10:34

3 Answers3

75

Since Java 7 you should use try-with-resources

try(Scanner sc1 = new Scanner(""); 
    Scanner sc2 = new Scanner(""); 
    Scanner sc3 = new Scanner("")){

}
//all scanners get closed implicitly

So you don't need any code at all.

The problem with all for-each or stream constructs is, that - theoretically - if the first close() fails with an exception on invoking the underlying source's close() method, the following scanners won't get closed. The Scanner.close() implementation catches any IOException, but no other exception that may occur.

The try-with-resources construct deals with that, the loops don't.


EDIT: While your question targeted at a more general approach, the above solution was a response to your particular problem: to deal with AutoCloseable resources, which should in any case be used with the try-with-resources construct, requiring no special treatment of the close method at all (= shortest solution to your particular problem).

Regarding the more general question about dealing with arbitrary items (which are no resources), Java has at least two options:

Creating a List from an Array/Varargs and iterate over it

for(YourItemType item : Arrays.asList(your,items,here)) {
  //do something
}

Creating a Stream from an Array/Varargs and apply functions to it

Stream.of(your,items,here).forEach(item -> { doSomething});

Of course the "doSomething" can be replaced with a method reference

Stream.of(your,items,here).forEach(this::myMethod);
...
void myMethod(YourItemType item){
  //doSomething
} 

The problem with that approach is, that checked exception have to be dealt with explicitly in lambda expressions. Lets take the above example and let myMethod throw a checked exception

void myMethod(YourItemType item) throws Exception

in that case your stream statement would have to look like

Stream.of(your,items,here).forEach(item -> {
  try {
    myMethod(item);
  } catch (Exception e){
    //omit or throw new RuntimeException(e);
  };

This doesn't look that nice. But we could put the lambda body in a separate method

void myMethodQuietly(YourItemType item) {
  try {
    myMethod(item);
  }catch(Exception e){
    //omit or throw new RuntimeException(e);
  }
}

Stream.of(your,items,here).forEach(this::myMethodQuietly);

This approach may be of interest to your particular resource problem. We can put all this into a CompositeAutoCloseable that takes resources created outside the class that all should be closed safely on invocation of close()

public class CompositeAutoCloseable implements AutoCloseable {

  private List<Closeable> resources;

  public CompositeAutoCloseable(Closeable... resources) {
    this.resources = Arrays.asList(resources);
    //you could use a stream here too
  }

  @Override
  public void close() {
      this.resources.stream().forEach(this::closeQuietly);
  }

  void closeQuietly(Closeable res) {
    if(res == null)  {
        return;
    }
    try {
        res.close();
    }catch(Exception e){
        //omit
    }
  } 
}

And once you have such a helper class, you can use it again with try-with-resources.

try(CompositeAutoCloseable cac = new CompositeAutoCloseable(sc1,sc2,sc3)) {
  //do something
}

I leave it up to you to decide if this makes sense compared to the inital solution ;)

Gerald Mücke
  • 10,724
  • 2
  • 50
  • 67
  • 1
    This answer seems to make the most sense to me. Leveraging `AutoCloseable`s instead of hard-streaming to explicitly close the `Scanner`s seems the most natural solution. – Mena May 04 '16 at 09:29
  • Note that try-with-resource still isn't really 100% safe. Here is a simple example from C# of where this might be problematic with the corresponding construct: http://stackoverflow.com/a/21123756/1348195 – Benjamin Gruenbaum May 04 '16 at 12:47
  • 6
    @Benjamin Gruenbaum: that post uses a lot of C# constructs that don’t exist in Java. Generally, it’s not the responsibility of the code *using* `Scanner` to fix potential bugs in the constructor of the class `Scanner`. Sure, it isn’t “100% safe”, but nothing is. How do you protect against someone pulling the plug? – Holger May 04 '16 at 13:55
  • This doesn't really answer the question. Of course you want to use `try` when you can, but that's not always an option. For example, when your class creates three `Scanner` instances in its constructor, and closes them in its own `close()` method. – BlueRaja - Danny Pflughoeft May 04 '16 at 15:50
  • 1
    @BlueRaja - Danny Pflughoeft: even then, you are better off using the `try` construct to ensure that all three are closed safely: `try(Scanner toClose1=sc1; Scanner toClose2=sc2) { sc3.close(); }` – Holger May 04 '16 at 16:25
  • When I asked the question I did want a more general solution as @Holger commented with but your solution did improve my code a lot. I feel that an example of the `Stream.of` variant would help to answer the question I originally had which is reflected in the title. Even though this answer seems to be most fitting, to the code example given, I originally sought the shortest way. – E. Sundin May 04 '16 at 19:22
3

If instantiation is separated from disposal, use Guava's com.google.common.io.Closer.

Basilevs
  • 22,440
  • 15
  • 57
  • 102
-1

Although there is sometimes a correlation between the length of the code and its quality that is not a good criterion to use to choose code.

I would probably use varargs and do it something like:

private void closeScanner(Scanner... scanners) {
    // Faff around with Java 8 in here if you like.
    for (Scanner s : scanners) {
        s.close();
    }
}

    // Some code.
    closeScanner(s, t, u);
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • Exception safety fail – Basilevs May 04 '16 at 18:26
  • @basilevs - Scanner.close does not throw exceptions. – OldCurmudgeon May 04 '16 at 19:38
  • @OldCurmudgeon: well, it throws only unchecked exceptions. – Steve Jessop May 04 '16 at 21:31
  • @SteveJessop - Then they will be caught elsewhere no? Still not clear how this is an exception safety fail. – OldCurmudgeon May 05 '16 at 08:58
  • 6
    If the first call to `close()` does throw an exception, then regardless of whether it's caught elsewhere, the second and third calls won't even be attempted. If you want resource cleanup to be attempted even following a failure, but it isn't attempted, that's what Basilevs means by an "exception safety fail" and that's what try-with-resources is designed to help do. – Steve Jessop May 05 '16 at 09:42