Get MethodHandle from lambda object - java

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.

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.

Java 8: How do functional Interfaces list hidden files?

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.

Given a `Class` object, get a method reference to `toString`

If you have only a Class object, how does one get a method reference to a method such as toString? Later we will have instances of this particular class on which we will invoke this method via the method reference.
For example, consider a Java enum, a subclass of Enum. Here T is defined as <T extends Enum>.
Class c = MyEnum.class
…
Function< T , String> f = c :: toString ;
I get an error saying "invalid method reference".
For toString, it's as easy as Object::toString. All Objects have toString, so you can use it right there. For other methods where you don't know statically that the object has that method, there's no easy way; you have to write a lambda that does it the ugly reflective way.
If you can access your Class object using a class literal in the form Class<MyEnum> c=MyEnum.class;, it implies that the type MyEnum is known at compile time. In this case, the Class object is an unnecessary detour. You can access all methods of the class MyEnum using the form MyEnum::methodName, e.g.
Function<MyEnum,String> f=MyEnum::toString;
This is what the tutorial describes as “Reference to an Instance Method of an Arbitrary Object of a Particular Type”. It doesn’t require an actual MyEnum instance.
Nevertheless, there is no point in dealing with MyEnum when you want to have a Function<T,…> as that function must be able to consume arbitrary instances of T, not necessarily being MyEnum. So this function can only use methods existing in T and it doesn’t need to search them in MyEnum.
Since your target method is not specific to MyEnum, that’s possible:
Function<T,String> f=T::toString;
But as already pointed out, the method toString is defined in java.lang.Object, so you may also use the form
Function<T,String> f=Object::toString;
as a method declared for all objects may also get invoked on instances of T. Though even this is a bit pointless as you can also use
Function<Object,String> f=Object::toString;
reflecting the ability to consume any instance of Object, not just T. Carefully written Generic code will always use wildcards to avoid unnecessary restrictions regarding its input. So it will accept a function which can consume T (which implies the ability to consume MyEnum) without requiring its type parameter to exactly match that type. For example:
<R> Stream<R> map(Function<? super T,? extends R> mapper)
map, applied on a Stream<T> will accept a Function<Object,…> as Object is a supertype of T…
So you can use T::methodName to access every method available for the bounds of that type, i.e. in your case you can use all methods of Enum and, of course, Object, but no methods specific to MyEnum not present in its supertypes. This isn’t different to ordinary method invocations you try to apply on instances of T. Further, a method not present in T wouldn’t be eligible for creating a valid Function<T,…> anyway.
If you want to create Function instances for methods not known at compile-time, you will have to use Reflection, and this is the only case where you have to deal with Class objects (in the context of method references). The second example of this answer shows how to create a Function for an instance method returning an object but that’s really only for those who know precisely what they are doing…
Note also that such reflectively created Functions have to use raw types as their appropriate generic type can’t be declared as it would refer to a type not present at compile-time.
What you are trying here, is to get the reference of toString() for the class Class, which is probably not what you intended. As toString() is defined for all objects, this should work (not tested):
Function< T , String> f = t -> t.toString();

Categories

Resources