18

I am going through the lambda expression in java 8

when i changed the code of thread it's working fine

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("run");
    }
}).start();

is converted to lambda expression as

new Thread(
    () -> System.out.println("Hello from thread")
).start();

But i am not able to convert the FilenameFilter Expression

File file = new File("/home/text/xyz.txt");
file.list(new FilenameFilter() {
    @Override
    public boolean accept(File dir, String name) {
        name.endsWith(".txt");
        return false;
    }
});

and unsuccessfully converted to this as

file.list(new FilenameFilter () {
    (File a1, String a2) -> { 
        return false;
    }
});

it's giving error as in eclipse as

Multiple markers at this line
- Syntax error, insert ";" to complete Statement
- Syntax error, insert "}" to complete Block
- Syntax error, insert "AssignmentOperator Expression" to complete Assignment

Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
SarthAk
  • 1,628
  • 3
  • 19
  • 24

4 Answers4

51

First things first, your formatting is horrible, sort it out!

Now, lambda syntax; to convert the anonymous class:

final FilenameFilter filter = new FilenameFilter() {
    @Override
    public boolean accept(File dir, String name) {
        return false;
    }
};

We start by replacing the anonymous class with an equivalent lambda for the single method accept(File dir, String name):

final FilenameFilter filter = (File dir, String name) -> {
    return false;
};

But we can do better, we don't need to define the types - the compiler can work those out:

final FilenameFilter filter = (dir, name) -> {
    return false;
};

And we can do better still, as the method return a boolean; if we have a single statement that evaluates to a boolean we can skip the return and the braces:

final FilenameFilter filter = (dir, name) -> false;

This can be any statement, for example:

final FilenameFilter filter = (dir, name) -> !dir.isDirectory() && name.toLowerCase().endsWith(".txt");

However, the File API is very old, so don't use it. Use the nio API. This has been around since Java 7 in 2011 so there is really no excuse:

final Path p = Paths.get("/", "home", "text", "xyz.txt");
final DirectoryStream.Filter<Path> f = path -> false;
try (final DirectoryStream<Path> stream = Files.newDirectoryStream(p, f)) {
    stream.forEach(System.out::println);
}

And in fact your example has a specific method built into Files that takes a Glob:

final Path p = Paths.get("/", "home", "text", "xyz.txt");
try (final DirectoryStream<Path> stream = Files.newDirectoryStream(p, "*.txt")) {
    stream.forEach(System.out::println);
}

Or, using the more modern Files.list:

final Path p = Paths.get("/", "home", "text", "xyz.txt");
final PathMatcher filter = p.getFileSystem().getPathMatcher("glob:*.txt");
try (final Stream<Path> stream = Files.list(p)) {
    stream.filter(filter::matches)
          .forEach(System.out::println);
}

Here filter::matches is a method reference because the method PathMatcher.matches can be used to implement the functional interface Predicate<Path> as it takes a Path and returns a boolean.


As an aside:

f.list(new FilenameFilter() {

    @Override
    public boolean accept(File dir, String name) {
        name.endsWith(".txt");
        return false;
    }
});

This makes no sense...

Brian Goetz
  • 90,105
  • 23
  • 150
  • 161
Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
  • 1
    Any reasoning behind not using the `File` API just because it's old? – citizen conn Feb 12 '16 at 20:30
  • 4
    @citizenconn first and foremost; the `File` API has abysmal error reporting, most methods return a `boolean`, leaving it up to you to work out why an operation failed. Other issues are lack of features, the NIO API has a huge number of utility functions for almost anything. Finally, actually _reading_ from files using the old API is verbose and errorprone - NIO greatly simplifies the whole process, for example the default charset is UTF-8 rather than whatever your current platform decides. And that's just for starters... – Boris the Spider Feb 12 '16 at 22:41
  • nio APIs are not working in Android 7 OS, I am getting error – Ramireddy Chintalapudi Oct 22 '19 at 08:45
8

It should be simpler :

f.list((File a1, String a2) -> {return false;});

or even :

f.list((a1,a2) -> {return false;});

The lambda expression replaces the instantiation of the abstract class instance.

Alexis C.
  • 91,686
  • 21
  • 171
  • 177
Eran
  • 387,369
  • 54
  • 702
  • 768
8

FileNameFilter is a functional interface. You don't need to instantiate it explicitly.

    f.list((dir, name) -> name.endsWith(".txt"));

Note also, that f should be a directory, not a file as in your example. Your example where f1 is a file will return null with the specified filter.

T.Gounelle
  • 5,953
  • 1
  • 22
  • 32
  • Exactly the one line solution that I was looking for when I found the "java 8 lambda expression for FilenameFilter" page. Thank you. – jschreiner May 13 '17 at 11:06
1

You don't have to put the class name, if you use a lambda-expression:

 f.list(
    (File a1, String a2) -> { 
        return false; }
    );

In fact, in your first example, you omit new Runnable().

lodo
  • 2,314
  • 19
  • 31