Are Java method references stable? [duplicate] - java

This question already has answers here:
Is there a way to compare lambdas?
(3 answers)
How do I remove lambda expressions/method handles that are used as listeners?
(2 answers)
Does a lambda expression create an object on the heap every time it's executed?
(3 answers)
When does JVM decide to reuse old lambda?
(5 answers)
How will Java lambda functions be compiled?
(2 answers)
Closed 4 years ago.
If I obtain a method reference using the new syntax:
anObject::aMethod
Do I always get the same object? That is, can I trust that two references to the same method will be equal?
This is good to know if, for example, I plan to use them as Runnable callbacks that I can add and remove:
someLibrary.addCallback(anObject::aMethod)
// later
someLibrary.removeCallback(sameObject::sameMethod)
Would this require saving the reference in a Runnable variable to keep it stable?

JLS makes no promises about identity or equality of what you get out of method reference expressions.
You can run a quick test:
Object obj = new Object();
IntSupplier foo = obj::hashCode;
IntSupplier bar = obj::hashCode;
System.out.println(foo == bar); // false
System.out.println(foo.equals(bar)); // false
But this is, of course, implementation dependent.
You could make your lambda Serializable and key your callback map with the serlialized representation. See How to serialize a lambda?. While this will work, it's not exactly required to work by the specs.

Just try this out to get the answer:
Object object = ...;
Supplier<String> s1 = object::toString;
Supplier<String> s2 = object::toString;
System.out.println(s1.equals(s2));
And the answer is... unfortunately not.
OF course if you keep the same reference (i.e. the same object), it will work; but if, as the example above, you request two lambdas, although they seem to be identical, they will never be equal.
Therefore reference = object::methodand then later remove(reference) will obviously work, but remove(sameObject::sameMethod) from a collection will never work if it is written literaly as such.
The answer is also no for constructor (e.g. ArrayList::new) and unbound methods (e.g. Object::toString). It seems that a new lambda is constructed each time you use a lambda expression.
As #Hitobat points it out, this unequality makes sense if you think about what exactly are lambdas and where do they come from. Basicly, Supplier<String> x = myObject::toString is a syntactic suggar for Supplier<String> x = new Supplier<String>( ... ). Without a proper Object.equals overloading, two instances of an anonymous class are obviously different. As many people probably, I though that there was a kind of cache of frequently used lambdas somewhere to make it more efficient; well, not at all.

Related

Java 8+ Unpacking Primitives From Singleton Objects Using Lambda or Stream? Structured Binding in Java? [duplicate]

This question already has answers here:
Java object destructuring
(2 answers)
Closed 1 year ago.
Effectively I need to take a subset of data out of a function return, but I don't actually need the returned object. So, is there a way to simply take what I need with a Stream or a Lambda-like syntax? And yes, I understand you CAN use stream().map() to put values into an array, and then decompose the array into named variables, but that's even more verbose for functionally equivalent code.
In C++, this would be called Structured Binding, but I seem to be missing the Java term for it when I search.
Procedural code I seem to be forced to write:
HandMadeTuple<Integer, Integer, ...> result = methodICurrentlyCall(input);
int thing1 = result.getFirst();
int thing2 = result.getSecond();
...
//thingX's get used for various purposes. Result is never used again.
Declarative code I'd like to be able to write:
int thing1, thing2;
(methodICurrentlyCall(input)) -> (MyType mt) { thing1 = mt.getFirst(); thing2 = mt.getSecond(); };
Or
int thing1, thing2;
Stream.of(methodICurrentlyCall(input)).forEach((mt) -> { thing1 = mt.getFirst(); thing2 = mt.getSecond();}
There's a really awkward hack: arrays.
final int[] thing1 = {0}, thing2 = {0};
Stream.of(methodICurrentlyCall(input))
.forEach(mt -> {
thing1[0] = mt.getFirst();
thing2[0] = mt.getSecond();
});
The array reference is final, therefore you can use it in lambdas.
I don't recommend this! It is neither shorter nor easier to understand
(Another option is using AtomicReference<T> or AtomicInteger/AtomicLong instead of the array, but there's a minimal overhead for guaranteeing atomic updates)
If you are looking for pattern matching or destructuring support for Java, this could be implemented in future versions of Java, see this related question on SO: Java object destructuring (even more details at https://cr.openjdk.java.net/~briangoetz/amber/serialization.html)

Java 8 method references for multiple statements in streams [duplicate]

This question already has answers here:
Multiple lambda method references
(3 answers)
Closed 5 years ago.
list.stream().forEach(e -> method(e)) can be converted to list.stream().forEach(this::method)
Similarly can we convert list.stream().forEach(e -> { method1(e); method2(e);}); using method references expressions. Big apologies if you don't understand question. I am using mobile app first time.
No you cannot.
The point of Method references in Java is to abstract (syntaxically) a lambda expression. Since forEach consumes a function that takes 1 element of type specified by the parent stream, there is no syntax sugar for double application using method references.
Even I'm not sure that this answer is wanted by you,
How about changing the method to static one in that class?

Mutating `free variables` of Lambda Expressions

I'm reading this fantastic article about Lambda Expressions and the following is uncleared to me:
Does Lambda Expression saves the value of the free-variables or refernse/pointer to each of them? (I guess the answer is the latter because if not, mutate free-variables would be valid).
Don't count on the compiler to catch all concurrent access errors. The
prohibition against mutation holds only for local variables.
I'm not sure that self experimenting would cover all the cases so I'm searching for a well defined rules about:
What free varibles can be mutated inside the Lambda Expression (static/properties/local variables/parameters) and which can be mutated out side while beeing used inside a Lambda Expression?
Can I mutate every free variable after the end of a block of a Lambda Expression after I used it (read or called one of his methods) inisde a Lambda Expression?
Don't count on the compiler to catch all concurrent access errors. The
prohibition against mutation holds only for local variables.
If
matchesis an instance or static variable of an enclosing class, then
no error is reported, even though the result is just as undefined.
Does the result of the mutation is undefined even when I use a synchroniziton algorithm?
Update 1:
free variables - that is, the variables that are not parameters and not defined inside the code.
In simple words I can conclude that Free variables are all the variables that are not parameters of the Lambda Expression and are not defined inside the same Lambda Expression ?
This looks like complicated "words" on a simpler topic. The rules are pretty much the same as for anonymous classes.
For example the compiler catches this:
int x = 3;
Runnable r = () -> {
x = 6; // Local variable x defined in an enclosing scope must be final or effectively final
};
But at the same time it is perfectly legal to do this(from a compiler point of view):
final int x[] = { 0 };
Runnable r = () -> {
x[0] = 6;
};
The example that you provided and uses matches:
List<Path> matches = new ArrayList<>();
List<Path> files = List.of();
for (Path p : files) {
new Thread(() -> {
if (1 == 1) {
matches.add(p);
}
}).start();
}
has the same problem. The compiler does not complain about you editing matches(because you are not changing the reference matches - so it is effectively final); but at the same time this can have undefined results. This operation has side-effects and is discouraged in general.
The undefined results would come from the fact that your matches is not a thread-safe collection obviously.
And your last point : Does the result of the mutation is undefined even when I use a synchroniziton algorithm?. Of course not. With proper synchronization updating a variable outside lambda(or a stream) will work - but are discouraged, mainly because there would be other ways to achieve that.
EDIT
OK, so free variables are those that are not defined within the lambda code itself or are not the parameters of the lambda itself.
In this case the answer to 1) would be: lambda expressions are de-sugared to methods and the rules for free-variables are the same as for anonymous classes. This has been discussed numerous times, like here. This actually answers the second question as well - since the rules are the same. Obviously anything that is final or effectively final can be mutated. For primitives - this means they can't be mutated; for objects you can't mutate the references (but can change the underlying data - as shown in my example). For the 3) - yes.
Your term “free variables” is misleading at best. If you’re not talking about local variables (which must be effectively final to be captured), you are talking about heap variables.
Heap variables might be instance fields, static fields or array elements. For unqualified access to instance variables from the surrounding context, the lambda expression may (and will) access them via the captured this reference. For other instance fields, as well as array elements, you need an explicit access via a variable anyway, so it’s clear, how the heap variable will be accessed. Only static fields are accessed directly.
The rules are simple, unless being declared final, you can modify all of them, inside or outside the lambda expression. Keep in mind that lambda expressions can call arbitrary methods, containing arbitrary code anyway. Whether this will cause problems, depends on how you use the lambda expressions. You can even create problems with functions not directly modifying a variable, without any concurrency, e.g.
ArrayList<String> list=new ArrayList<>(Arrays.asList("foo", "bar"));
list.removeIf(s -> list.remove("bar"));
may throw a java.util.ConcurrentModificationException due to the list modification in an ongoing iteration.
Likewise, modifying a variable or resource in a concurrent context might break it, even if you made sure that the modification of the variable itself has been done in a thread-safe manner. It’s all about the contracts of the API you are using.
Most notably, when using parallel Streams, you have to be aware that functions are not only evaluated by different threads, they are also evaluating arbitrary elements of the Stream, regardless of their encounter order. For the final result of the Stream processing, the implementation will assemble partial results in a way that reestablishes the encounter order, if necessary, but the intermediate operations evaluate the elements in an arbitrary order, hence your functions must not only be thread safe, but also not rely on a particular processing order. In some cases, they may even process elements not contributing to the final result.
Since your bullet 3 refers to “after the end of a block”, I want to emphasize that it is irrelevant at which place inside your lambda expression the modification (or perceivable side effect) happens.
Generally, you are better off with functions not having such side effects. But this doesn’t imply that they are forbidden in general.

101: What does baseApp = (MyApp)this.getContext() do? [duplicate]

This question already has answers here:
What does this block of code do?
(6 answers)
Closed 8 years ago.
When I do this:
baseApp = (MyApp)this.getContext();
What am I actually doing?
as opposed to doing:
baseApp = myApp.doSomething();
I'm not concern with the methods but understanding the construction.
How are those 2 above different and why?
Whats the meaning of doing (MyApp)?
Whats the meaning of doing (MyApp)?
It is a reference type cast.
It checks that the reference produced by evaluating the RHS (i.e. this.getContext() ) is compatible with MyApp and then uses it that as the result of the expression (with that type). If the reference given by the RHS expression is not for a compatible type, a runtime exception will be thrown.
By contrast ...
baseApp = myApp.doSomething();
is just calling the doSomething() method and assigning it ... WITHOUT doing a typecast. If the doSomething() method does not deliver a value of the correct type, you will get a compilation error.
For the record, there is no "instantiation" going on here. Instantiation is done using the new operator1.
1 - ... or by calling specific reflective methods.
Firstly where are you calling this from? An Activity?
The first line is casting the current objects context to a MyApp object then assigning it to an object named baseApp. And I also assume baseApp is of type MyApp.
The second line is assigning the value returned from the method named doSomething() to baseApp
but more information is needed to compare further.

Java is NEVER pass-by-reference, right?...right? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is Java “pass-by-reference”?
I found an unusual Java method today:
private void addShortenedName(ArrayList<String> voiceSetList, String vsName)
{
if (null == vsName)
vsName = "";
else
vsName = vsName.trim();
String shortenedVoiceSetName = vsName.substring(0, Math.min(8, vsName.length()));
//SCR10638 - Prevent export of empty rows.
if (shortenedVoiceSetName.length() > 0)
{
if (!voiceSetList.contains("#" + shortenedVoiceSetName))
voiceSetList.add("#" + shortenedVoiceSetName);
}
}
According to everything I've read about Java's behavior for passing variables, complex objects or not, this code should do exactly nothing. So um...am I missing something here? Is there some subtlety that was lost on me, or does this code belong on thedailywtf?
As Rytmis said, Java passes references by value. What this means is that you can legitimately call mutating methods on the parameters of a method, but you cannot reassign them and expect the value to propagate.
Example:
private void goodChangeDog(Dog dog) {
dog.setColor(Color.BLACK); // works as expected!
}
private void badChangeDog(Dog dog) {
dog = new StBernard(); // compiles, but has no effect outside the method
}
Edit: What this means in this case is that although voiceSetList might change as a result of this method (it could have a new element added to it), the changes to vsName will not be visible outside of the method. To prevent confusion, I often mark my method parameters final, which keeps them from being reassigned (accidentally or not) inside the method. This would keep the second example from compiling at all.
Java passes references by value, so you get a copy of the reference, but the referenced object is the same. Hence this method does modify the input list.
The references themselves are passed by value.
From Java How to Program, 4th Edition by Deitel & Deitel: (pg. 329)
Unlike other languages, Java does not allow the programmer to choose whether to pass
each argument by value or by reference. Primitive data type variables are always passed
by value. Objects are not passed to methods; rather, references to objects are passed to
methods. The references themselves are passed by value—a copy of a reference is passed
to a method. When a method receives a reference to an object, the method can manipulate
the object directly.
Used this book when learning Java in college. Brilliant reference.
Here's a good article explaining it.
http://www.javaworld.com/javaworld/javaqa/2000-05/03-qa-0526-pass.html
Well, it can manipulate the ArrayList - which is an object... if you are passing an object reference around (even passed by value), changes to that object will be reflected to the caller. Is that the question?
I think you are confused because vsName is modified. But in this context, it is just a local variable, at the exact same level as shortenedVoiceSetName.
It's not clear to me what the exact question within the code is. Java is pass-by-value, but arrays are pass-by-reference as they pass no object but only pointers! Arrays consist of pointers, not real objects. This makes them very fast, but also makes them dangerous to handle. To solve this, you need to clone them to get a copy, and even then it will only clone the first dimension of the array.
For more details see my answer here: In Java, what is a shallow copy? (also see my other answers)
By the way, there are some advantages as arrays are only pointers: you can (ab)use them as synchronized objects!

Categories

Resources