Java 8: How do functional Interfaces list hidden files? - java

In a Java 8 introduction mini-book, I saw these implementation for listing hidden files:
File[] hiddenFiles = mainDirectory.listFiles(f -> f.isHidden());
and
File[] hiddenFiles = mainDirectory.listFiles(File::isHidden);
I checked the docs and it looks like listFiles accepts both FileFilter or FilenameFilter implementation. Both are functional interfaces.
From the Javadocs:
Functional Interface:
This is a functional interface and can therefore be used as the
assignment target for a lambda expression or method reference.
My questions: What happens in both cases (the expression versus the method reference), and how does Java adjust what I send to listFiles?
What I mean: In the source code, I can't find any indication whether listFiles checks for a method reference or lambda expression. I understand that Java internally checks and adjust both (lambda or method ref) to become FileFilter or FilenameFilter instances, as shown by the lambda expression and how it can be the accept method, but what happens internally with File::isHiiden? Is it wrapped with some kind of class, or is it transformed into expression such as the first case?
I know I'm kind of lost here, maybe confused, but I hope you could understand pretty much what my questions is.
Edit:
I found few useful statements from the docs (I changed a bit):
Evaluation of a method reference expression is distinct from
invocation of the method itself.
At run time, evaluation of a method reference expression is similar to
evaluation of a class instance creation expression
The class implements the targeted functional interface type
A new instance of a class with properties(depends on the expression) is
allocated and initialized, or an existing instance of a class with the
properties below is referenced
The value of a method reference expression is a reference to an
instance of a class with the following properties(depends on the
expression)
So for example in my case, where I use File::isHidden, an object will be created by a class that implements FileFilter, FileFilter have accept method that accepts File type objects and calls isHidden on those File type objects.

If you look at JLS 15.27.3, "Type of a Lambda Expression", you will know that the lambda expression is simply an expression that can be assigned to a variable of that functional interface (here your lambda is an expression of a type assignable to FileFilter. This is because your expression (File -> boolean) is congruent with the FileFilter functional interface, and not the FilenameFilter interface.
To answer the second part of your question, there is in fact wrapping in a class, as described in JLS 15.27.4.

Related

Method reference notation vs "standard" Lambda notation

I was expecting these to be simple drop-in replacements for each other, but they're not. Clearly I'm not understanding the notation.
Can anyone explain why does that happen?
playButton.setOnAction(e -> track.play());
Here, compiler is happy with play() having a signature of
void play()
but here
playButton.setOnAction(track::play);
it requires
void play(Event e)
Here is a quote from the Java language specification:
A method reference expression (§15.13) is potentially compatible with
a functional interface type T if, where the arity of the function type
of T is n, there exists at least one potentially applicable method
when the method reference expression targets the function type with
arity n (§15.13.1), and one of the following is true:
The method reference expression has the form ReferenceType :: [TypeArguments] Identifier and at least one potentially applicable
method is either (i) static and supports arity n, or (ii) not static
and supports arity n-1.
The method reference expression has some other form and at least one potentially applicable method is not static.
...
The definition of potential applicability goes beyond a basic arity
check to also take into account the presence and "shape" of functional
interface target types.
Every method reference should conform to a function interface (which is an interface that declares a single abstract method, non-overriding methods from Object class). The compiler needs to verify whether the provided reference resolves to a single existing method that has required arity (number of parameters) and their types match the types of the method declared by the target functional interface.
Let's have a look at the prior Java 8 code (code-sample from JavaFX tutorial by created Oracle):
button2.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
label.setText("Accepted");
}
});
That is the "shape" that needs to be filled with a code describing an action. Method handle() of the EventHandler interface expects an event as an argument. Whether it would be used or not, that's up to you, the key point is that the abstract method of the target interface expects this argument to be provided.
By using a lambda expression e -> track.play() you're explicitly telling to ignore it.
And when you're passing a method reference track::play, which should be classified as a reference to an instance method of a particular object (see), the compiler will try to resolve it to a method play(Event) and you're getting a compilation error because it fails to find one.
In this case, reference track::play is not an equivalent of lambda e -> track.play(), but () -> track.play(), which doesn't conform to the target functional interface EventHandler.
In case if you wonder, how a method reference which can be applicable to a non-static method of arity n-1 mentioned in the specification (see case ii) can look like, here is an example:
BiPredicate<String, String> startsWith = String::startsWith; // the same as (str1, str2) -> str1.strarsWith(str2);
System.out.println(startsWith.test("abc", "a")); // => true
System.out.println(startsWith.test("fooBar", "a")); // => false
And you can construct a similar reference which conforms to EventHandler interface and applicable an instance method of arity n-1 using one of the parameterless methods of the Event type. It's not likely to be useful in practice, but it would be valid from the compiler perspective of view, so feel free to try it as an exercise.

What is the background behind the following statement "A lambda is an object without an identity" in Java 8?

Would appreciate any help on understanding below two concepts in Java 8.
What I know
A lambda is an object without an identity and should not be used as a regular object.
A lambda expression should not be calling methods from Object class like toString, equals, hashCode etc.
What I'd like know more about
What difference does lambda expression have to be called as an object without identity?
Why exactly should methods from Objectclass not be used while using lambda expression?
1) A lambda is an object without an identify and should not be used as a regular object.
This is not true. A lambda is a Java object, and it does have an identity1. The problem is that the lifecycle is deliberately left specified to allow Java compilers freedom to optimize the code that evaluates them. Therefore, you cannot know when new lambda objects are going to be created, or existing ones are going to be reused.
JLS 15.27.4 says:
"At run time, evaluation of a lambda expression is similar to evaluation of a class instance creation expression, insofar as normal completion produces a reference to an object. Evaluation of a lambda expression is distinct from execution of the lambda body.
Either a new instance of a class with the properties below is allocated and initialized, or an existing instance of a class with the properties below is referenced. ... "
2) A lambda expression should not be calling methods from Object class like toString, equals, hashCode etc.
As written, that is also not true. A lambda expression may call those methods. However, it is not advisable to rely on those methods to have any specific behavior when you call them on a lambda object
The JLS states:
"The class ... may override methods of the Object class."
In other words, it may ... or may not ... override them. If you rely a particular behavior of these methods, your application is (in theory) non-portable.
Furthermore, since the instantiation semantics are also unspecified, the behavior of Object::equals and Object::hashCode are uncertain.
Finally, it is unspecified whether lambdas are clonable.
1 - Sure, a lambda doesn't have a name: it is anonymous. But name and identity are different concepts.
Basically, a lambda is a convenience of doing this:
#FunctionalInterface
interface A {
void b();
}
void c(A a) {
...
}
void main() {
c(new A() {
void b() {
...
}
});
}
I apologize for the less than stellar variable names, but as you can see, A is an interface with one method. c is a method that takes in an A interface. However, instead of creating your own class that implements A, you can create it on the spot. This is what you call an anonymous class, since it doesn't have a name. This is where the quote you have:
A lambda is an object without an identify
comes from. The class doesn't have an identity. The reason it relates to lambdas is because if an interface has only one method, you can use lamdas to simplify it.
void main() {
c(
() -> {
...
}
);
}
This is the exact same as before.
The second part, why lambdas shouldn't use Object's methods, I didn't know before. You should probably have someone else answer this, however my guess is that lambda classes don't look like it extends Object directly, so you can't use it's methods.

Lambda expressions mechanisms in Java [duplicate]

This question already has answers here:
Lambda expression vs method reference implementation details
(3 answers)
Closed 5 years ago.
I just read in a book that when a lambda expression is assigned to a functional interface, then that sets the "target type" for the lambda and an instance of that type (that is, the functional interface's type) is created with the lambda expression used as implementation for the abstract method in the functional interface.
My question: If so, then does that mean lambdas aren't really standalone methods and as such a new type of element brought into the language, but are simply a more compact way for expressing an anonymous class and as such merely are added facility (just like generics) on the compiler's side?
Moreover, how do method references comply with that, in particular, static methods which are not associated with any objects? For example, when a method reference to an instance method is assigned to a functional interface then the encapsulating object for that method is used, but what happens in the case of a static method - those are not associated with any object.. ?
If so, then does that mean lambdas aren't really standalone methods and as such a new type of element brought into the language,
Correct, lambdas are compiled into normal methods with a synthetic name
but are simply a more compact way for expressing an anonymous class and as such merely are added facility (just like generics) on the compiler's side?
No, it's not only on the compiler side. There are is also code in the JVM involved, so that the compiler doesn't have to write class files for the lambdas.
Moreover, how do method references comply with that, in particular, static methods which are not associated with any objects?
Method references are not different from lambdas: at runtime there has to be an object implementing the functional interface. Upon calling the "SAM" of the object this method will call the referenced method.
For example, when a method reference to an instance method is assigned to a functional interface then the encapsulating object for that method is used,
No, it can't be used. Let's take the following example using a System.out::println method reference:
Arrays.asList("A", "B").forEach(System.out::println);
List<E>.forEach() expects a Consumer<? super E> which defines the method void accept(E e). The compiler need to generate byte code and other information in the class file so that at runtime the JVM can generate a class implementing Consumer<E> with a method void accept(E e). This generated method then calls System.out.println(Object o).
The runtime generated class would look something like
class $$lambda$xy implements Consumer<Object> {
private PrintStream out;
$$lambda$xy(PrintStream out) {
this.out = out;
}
void accept(Object o) {
out.println(o);
}
}
Your question from the comment: "Why not directly assign to instance and its method?"
Let's expand the example a little bit:
static void helloWorld(Consumer<String> consumer) {
consumer.apply("Hello World!");
}
public static void main(String[] args) {
helloWorld(System.out::println);
}
To compile this, the compiler has to generate bytecode that creates an object implementing Consumer<String> (so it can pass the object into helloWorld()). That object somehow has to store the information that upon calling it's accept(x) method it has to call println(x) on the System.out PrintStream.
Other languages may have other names or concepts for this kind of objects - in Java the established concept is "an anonymous class implementing the interface and an object of that anonymous class".
How does the object store this information? Well, you could invent some super cool new way to store this information. The Java Language designers decided that an anonymous class would be good enough - for the time being. But they had the foresight that if someone came along with a new idea to implement it in a more efficient way, this should be easy to integrate into the Java ecosystem (Java compiler and JVM).
So they also decided to create that anonymous class not at compile time but to let the compiler just write the necessary information into the class file. Now the JVM can at runtime decide on what the optimal way to store the information (calling the correct method on the correct object) is.
For example, when a method reference to an instance method is assigned
to a functional interface then the encapsulating object for that
method is used, but what happens in the case of a static method -
those are not associated with any object..
That depends on context. Let say we have a static Utils#trim(String) method that will obviously trim given string.
And now, lest have a List<String> list and lets have some strings in it. We can do something like this:
list.stream().map(Utils::trim).collect(Collectors.toList());
As you can see, in given context, we are using lambda static method reference in order to use every string in list as input argument of Utils::trim method.

Get MethodHandle from lambda object

From java.lang.invoke.LambdaMetafactory:
The recommended mechanism for evaluating lambda expressions is to desugar the lambda body to a method, invoke an invokedynamic call site whose static argument list describes the sole method of the functional interface and the desugared implementation method, and returns an object (the lambda object) that implements the target type.
And from inspection this is at least what Oracle JDK does.
My question: given a lambda object is there a way to find the name (or a handle to) the implementation method? Alternately, given a list of implementation methods, is there a way to tell which one corresponds to a given lambda object?
You could access the ConstantPool of the lambda class (i.e. by using sun.misc.SharedSecrets.getJavaLangAccess().getConstantPool(class)) and then search for the method reference. You have to ignore the object constructor as well as autoboxing/unboxing method references, the remaining method reference is the implementing method.

Why can't I assign lambda to Object?

I was trying to assign a lambda to Object type:
Object f = ()->{};
And it gives me error saying:
The target type of this expression must be a functional interface
Why is this happening, and how to do this?
It's not possible. As per the error message Object is not a functional interface, that is an interface with a single public method so you need to use a reference type that is, e.g.
Runnable r = () -> {};
This happens because there is no "lambda type" in the Java language.
When the compiler sees a lambda expression, it will try to convert it to an instance of the functional interface type you are trying to target. It's just syntax sugar.
The compiler can only convert lambda expressions to types that have a single abstract method declared. This is what it calls as "functional interface". And Object clearly does not fit this.
If you do this:
Runnable f = (/*args*/) -> {/*body*/};
Then Java will be able to convert the lambda expression to an instance of an anonymous class that extends Runnable. Essentially, it is the same thing as writing:
Runnable f = new Runnable() {
public void run(/*args*/) {
/*body*/
}
};
I've added the comments /*args*/ and /*body*/ just to make it more clear, but they aren't needed.
Java can infer that the lamba expression must be of Runnable type because of the type signature of f. But, there is no "lambda type" in Java world.
If you are trying to create a generic function that does nothing in Java, you can't. Java is 100% statically typed and object oriented.
There are some differences between anonymous inner classes and lambdas expressions, but you can look to them as they were just syntax sugar for instantiating objects of a type with a single abstract method.

Categories

Resources