I'm a Java and SQL enthusiast. I have created jOOQ, a comprehensive SQL library for Java, and I'm blogging mostly about these three topics: Java, SQL and jOOQ. Lukas is a DZone MVB and is not an employee of DZone and has posted 193 posts at DZone. You can read more from them at their website. View Full User Profile

Java 8 Friday Goodies: java.io Finally Rocks!

01.13.2014
| 4343 views |
  • submit to reddit

At Data Geekery, we love Java. And as we’re really into jOOQ’s fluent API and query DSL, we’re absolutely thrilled about what Java 8 will bring to our ecosystem. We have blogged a couple of times about some nice Java 8 goodies, and now we feel it’s time to start a new blog series, the…

Java 8 Friday

Every Friday, we’re showing you a couple of nice new tutorial-style Java 8 features, which take advantage of lambda expressions, extension methods, and other great stuff. You’ll find the source code on GitHub.

Java 8 Goodie: java.io with Lambdas

Interacting with the file system has been a bit of a pain in Java. CTMMC shows us an example of how to copy a file with Java. While some issues still remain, at least, we can now use lambdas and the new Streams API totraverse the file system and list files! Here is the FileFilterGoodies example that we’ve pushed to our GitHub repository:

public class FileFilterGoodies {

    public static void main(String args[]) {
        listRecursive(new File("."));
    }

    /**
     * This method recursively lists all
     * .txt and .java files in a directory
     */
    private static void listRecursive(File dir) {
        Arrays.stream(dir.listFiles((f, n) ->
                     !n.startsWith(".")
                  &&
                     (f.isDirectory()
                  ||  n.endsWith(".txt")
                  ||  n.endsWith(".java"))
              ))
              .forEach(unchecked((file) -> {
                  System.out.println(
                      file.getCanonicalPath()
                          .substring(new File(".")
                              .getCanonicalPath()
                              .length()));

                  if (file.isDirectory()) {
                      listRecursive(file);
                  }
              }));
    }

    /**
     * This utility simply wraps a functional
     * interface that throws a checked exception
     * into a Java 8 Consumer
     */
    private static <T> Consumer<T>
    unchecked(CheckedConsumer<T> consumer) {
        return t -> {
            try {
                consumer.accept(t);
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }

    @FunctionalInterface
    private interface CheckedConsumer<T> {
        void accept(T t) throws Exception;
    }
}

The output of the above programme is:

\jOOQ's Java 8 Goodies.iml
\LICENSE.txt
\out
\out\production
\out\production\jOOQ's Java 8 Goodies
\out\production\jOOQ's Java 8 Goodies\org
\out\production\jOOQ's Java 8 Goodies\org\jooq
\out\production\jOOQ's Java 8 Goodies\org\jooq\java8
\out\production\jOOQ's Java 8 Goodies\org\jooq\java8\goodies
\out\production\jOOQ's Java 8 Goodies\org\jooq\java8\goodies\io
\out\production\jOOQ's Java 8 Goodies\org\jooq\java8\goodies\io\FileFilterGoodies$CheckedConsumer.class
\out\production\jOOQ's Java 8 Goodies\org\jooq\java8\goodies\io\FileFilterGoodies.class
\README.txt
\src
\src\org
\src\org\jooq
\src\org\jooq\java8
\src\org\jooq\java8\goodies
\src\org\jooq\java8\goodies\io
\src\org\jooq\java8\goodies\io\FileFilterGoodies.java

Now, that’s really awesome, isn’t it? Let’s decompose the abovelistRecursive() method:

// With this method, we wrap the File[] array
// into a new Java 8 Stream, which has awesome
// new methods.
Arrays.stream(

// The Java 1.2 File.listFiles() method luckily
// accepts a @FunctionalInterface, which can be
// instantiated using a lambda expression
// ...
// In this example, we'll just ignore the fact
// that listFiles can return null
              dir.listFiles((f, n) ->
             !n.startsWith(".")
          &&
             (f.isDirectory()
          ||  n.endsWith(".txt")
          ||  n.endsWith(".java"))
      ))

// Each Stream (and also java.util.List) has this
// awesome forEach method, that accepts a Consumer
      .forEach(

// Unfortunately, Java 8 Consumers don't allow
// throwing checked exceptions. So let's quickly
// wrap it (see details below) ...
               unchecked(

// ... and pass another lambda expression to it,
// which prints the local path and recurses
                         (file) -> {
          System.out.println(
              file.getCanonicalPath()
                  .substring(new File(".")
                      .getCanonicalPath()
                      .length()));

          if (file.isDirectory()) {
              listRecursive(file);
          }
      }));

More goodies next week

Stay tuned for next week, when we show you how Java 8 improves using XML with jOOX

Published at DZone with permission of Lukas Eder, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Martin Vaněk replied on Mon, 2014/01/13 - 1:29pm

I really enjoy reading that condition splattered on six lines with brackets sprinkled all over. Exactly this made LISP so popular and widespread language. Sorry for bitter irony, but this is more of puzzle then readable code.

Loren Kratzke replied on Tue, 2014/01/14 - 5:52pm

 I agree. No offense to the author. I think it is the technology at issue. I have zero clue how that code works. It is completely unreadable and I have been programming Java since v1.0.3. Lambdas make horrible, terrible code that I would never ever want to read, let alone debug. I imagine that a new programmer would see that and get very turned off. This is why I think this stuff is a bad idea, not because of how it works, but because of how horrible it looks. Just my opinion.

joshua long replied on Tue, 2014/01/14 - 8:08pm

wherever you see a lamba, just imagine an anonymous inner class containing one method whose implementation is provided by the lamba. Thus, 

CheckedConsumer<File> cf = (File f) -> {};
is the same as:
CheckedConsumer<File> cf = new CheckedConsumer<File>(){ 
   public void accept (File f) { }
} ;

and the anonymous variant, (T t)->{}, can be used wherever a CheckedConsumer<T> is expected. 

HTH in your parsing efforts.


Lukas Eder replied on Wed, 2014/01/15 - 3:41am in response to: Loren Kratzke

The original post had less horizontal space for the code, which is why there were "artificial" line breaks. Also, obviously, you don't have to write the whole statement contained in the lambda on a single line.

Fact is, the lambda solution is much more readable than the pre-Java 8 anonymous class solution. Of course, you could make your FileFilter is a top-level class if you prefer that.

Martin Vaněk replied on Wed, 2014/01/15 - 7:01am in response to: Lukas Eder

Oddly enough, I have no problem understanding code, it is just its the clutterness. 

I agree that anonymous inner classes are worse, but I believe that this piece of code can actualy be written more nicely without any of these two.

Lukas Eder replied on Wed, 2014/01/15 - 7:51am in response to: Martin Vaněk

Yes, with Java 8's API enhancements in java.nio.Files. Particularly, the walk() method, which returns a Stream is very useful.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.