Lambda this reference in java
You can't reference to this
in a lambda expression. The semantic of this
has been changed to reference the instance of the surrounding class only, from within the lambda. There is no way to reference to the lambda expression's this
from inside the lambda.
The problem is that you use this
in the main()
method. The main method is static and there is no reference to an object that represents this
.
When you use this
inside an instance of an inner class you are referencing to the instance of the inner class.
A lambda expression is not an inner class, this
is not referencing to the instance of the lambda expression. It is referencing to the instance of the class you define the lambda expression in. In your case it would be a instance of Main. But since your are in a static method, there is no instance.
This is what your second compilation error is telling you. You hand over an instance of Main to your method. But your method signature requires an instance of Observer.
Update:
The Java Language Specification 15.27.2 says:
Unlike code appearing in anonymous class declarations, the meaning of names and the this and super keywords appearing in a lambda body, along with the accessibility of referenced declarations, are the same as in the surrounding context (except that lambda parameters introduce new names).
The transparency of this (both explicit and implicit) in the body of a lambda expression - that is, treating it the same as in the surrounding context - allows more flexibility for implementations, and prevents the meaning of unqualified names in the body from being dependent on overload resolution.
Practically speaking, it is unusual for a lambda expression to need to talk about itself (either to call itself recursively or to invoke its other methods), while it is more common to want to use names to refer to things in the enclosing class that would otherwise be shadowed (this, toString()). If it is necessary for a lambda expression to refer to itself (as if via this), a method reference or an anonymous inner class should be used instead.
this' reference escape in case of lambda in constructor
In the original question the code
public class ThisEscape {
public ThisEscape(EventSource source) {
source.registerListener(
new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
});
}
}
is problematic because the object have been registered in the constructor and then may be used by the event managing system while not being fully constructed. In fact, this is dangerous only if doSomething
access something external to the ThisEscape
instance.
And this is the same with your lambda "equivalent"
public class ThisEscape {
public ThisEscape(EventSource source) {
source.registerListener(e -> doSomething(e));
}
}
But don't be fooled, anonymous inner classes are not strictly equivalent to lambdas... this
refers to the current instance in case of anonymous inner class but refers to the enclosing instance of the lambda (this
is in the closure of the lambda) :
interface doable {
public void doIt();
}
public class Outer {
public Outer() {
doable d1 = new doable() {
public void doIt() {
System.out.println(this);
}
};
d1.doIt();
doable d2 = ()->System.out.println(this);
d2.doIt();
}
public static void main(String []argv) {
new Outer();
}
}
produces something like :
Outer$1@3764951d
Outer@3cd1a2f1
The second line clearly shows that a lambda is not an instance of any class, it is not an object.
Variable referencing from a lambda expression in Java
The first example works, because a = true
is actually shorthand for this.a = true
, and this
is always final
(so says the Java Specification).
Getting an interface reference inside a lambda function
Since default lambda conversion gives you lambda with signature of () -> Unit
, that means underlying Runnable
is completely hidden.
You have to either deal with manual object creation, or write a wrapper extension function that will consume lambda with another signature:
// custom extension function for handler
inline fun Handler.postDelayed(delayMilis: Long, crossinline runnable: (Runnable) -> Unit) = postDelayed(object : Runnable{
override fun run() {
runnable(this)
}
}, delayMilis)
Then at calling side you will be provided with Runnable
object as lambda parameter (only parameter: it
):
hwnd.postDelayed(5000){
// it : Runnable
hwnd.postDelayed(it, 5000)
}
Or if you want to get really fancy, change extension parameter to Handler.(Runnable) -> Unit
, then you will be able to call:
hwnd.postDelayed(5000){
// this : Handler, it : Runnable
postDelayed(it, 5000)
}
How to make a self-referencing lambda function that also references locals in Java?
Although I do not understand why such a construct would be needed, you could do:
Consumer<String> func = argument -> {}; // pre-declare func
List<Consumer<String>> one = new ArrayList<>(Collections.singletonList(func));
one.set(0, argument -> {
async_fn(one.get(0)); // Call an function with func as callback
});
Or use an array.
new Keyword In Java Lambda Method Reference
String::toUpperCase
is a method reference that can be applied to any String
instance.
new String()::toUpperCase
is a method reference that can be applied to a specific String
instance (the instance created by new String()
).
Since UnaryOperator<String>
expects a method that takes a String
and returns a String
, String::toUpperCase
fits (since you can apply it on a String
and get the upper case version of that String
).
On the other hand, new String()::toUpperCase
doesn't fit UnaryOperator<String>
, since it is executed on an already specified String
, so you can't pass another String
instance to it.
It can, however, by assigned to a Supplier<String>
, since it simply supplies an empty String
instance:
Supplier<String> emptyStringToUpperCase = new String()::toUpperCase;
This is similar to:
Supplier<String> emptyStringToUpperCase = () -> new String().toUpperCase();
while this:
UnaryOperator<String> stringToUpperCase = String::toUpperCase;
is similar to:
UnaryOperator<String> stringToUpperCase = s -> s.toUpperCase();
What would be the problem in providing this keyword for Java's lambda body?
The Java Language Specification 15.27.2 says:
Unlike code appearing in anonymous class declarations, the meaning of names and the
this
andsuper
keywords appearing in a lambda body, along with the accessibility of referenced declarations, are the same as in the surrounding context (except that lambda parameters introduce new names).The transparency of
this
(both explicit and implicit) in the body of a lambda expression - that is, treating it the same as in the surrounding context - allows more flexibility for implementations, and prevents the meaning of unqualified names in the body from being dependent on overload resolution.Practically speaking, it is unusual for a lambda expression to need to talk about itself (either to call itself recursively or to invoke its other methods), while it is more common to want to use names to refer to things in the enclosing class that would otherwise be shadowed (
this
,toString()
). If it is necessary for a lambda expression to refer to itself (as if viathis
), a method reference or an anonymous inner class should be used instead.
Java method reference with return result using lambda expression
Using BooleanSupplier
as proposed by @Holger is a possible solution.
However I would recommend using an IntPredicate
, because this allows you to pass the testValue from handleSomething
to the predicate:
import java.util.function.IntPredicate;
public class Test {
public static void main(String[] args) {
int testValue = 23;
handleSomething(true, testValue, Test::checkIfZero);
handleSomething(false, testValue, Test::checkIfLargerThanZero);
}
private static boolean checkIfZero(int value) {
if (value == 0)
return true;
return false;
}
private static boolean checkIfLargerThanZero(int value) {
if (value > 0)
return true;
return false;
}
private static void handleSomething(boolean test, int value, IntPredicate function) {
if (test) {
System.out.println("Ignore");
return;
}
if (function.test(value))
System.out.println("Passed");
else
System.out.println("Failed");
}
}
Related Topics
Java Wait for Thread to Finish
How to Make Notepad to Save Text in Utf-8 Without the Bom
How to Parse Datetime-String with Am/Pm Marker
Java Date - Insert into Database
How to Format Localdate Object to Mm/Dd/Yyyy and Have Format Persist
How to Make My Swingworker Example Work Properly
Arraylist Initial Capacity and Indexoutofboundsexception
Bidirectional Multi-Valued Map in Java
Using Jasperreports with a Relative Path
Javafx Automatic Resizing and Button Padding
Why Does List<String>.Toarray() Return Object[] and Not String[]? How to Work Around This
Date Format Parse Exception - "Eee Mmm Dd Hh:Mm:Ss Z Yyyy"
Returning Overlapping Regular Expressions
Drawing in Jlayeredpane Over Exising JPAnels
How to Display a Svg Byte Array as an Image in a Jasperreport