As I tested, the below code executes without any issues. But I could not understand the logic. Can someone please explain?
public static void main(String[] args) {
List<String> london = new ArrayList<>(Arrays.asList("Chinatown","Croydon","Eden Park"));
class City{
private String name;
City(String name){
this.name = name;
}
public void printCity(){
System.out.println(name);
}
}
london.stream().map(City::new).forEach(City::printCity); //Chinatown,Croydon,Eden Park
}
In the above example code, I have following questions.
The foreach method always takes a consumer object. In here printCity method is not a method that takes an argument. Still it works. How?
printCity method is not a static method here. How a City itself can call a instance method?
In your last statement you have currently used method references. This can be alternatively written as following:
london.stream().map(name -> new City(name)).forEach(city -> city.printCity());
As you can see above, the map returns City instances. So forEach gets City instances and calls printCity on each instance.
When using method references, City::printCity is not the same as calling City.printCity. See Method References documentation
Method references are just easy-to-read lambda expressions.
The consumer is equivalent to something like c -> c.printCity(), (City c) -> c.printCity(), or some long anonymous class that is a massive pain to type out.
The city instances are the subject of invocation. There is just syntactical sugar in the form of City::printCity which passes the method to the expression, where the instance is called.
Related
public class X{
public static void print(Integer n, Function<Integer, String> fn) {
System.out.println(fn.apply(n));
}
public static void main(String []args){
print(3, Integer::repeat()); //
}
}
Example:
Since 3 is a given number, the value returned by the function and printed on the console should be "aaa".
Your method reference is wrong in a couple ways. First, a method reference doesn't end in parentheses (). Second, the Integer class doesn't have any repeat method.
The String class defines the repeat method, available for the first time in Java 11. But you want to execute on a specific instance of a string, namely the constant "a".
Try "a"::repeat.
Method references as well as lambda expressions should conform to a function interface, by providing an implementation of the abstract method defined by this interface.
And interface Function declares a method apply()
R apply(T t);
You can implement it like that with lambda expression:
Function<Integer, String> fun = num -> String.valueOf(num);
Method repeat() is an instance method of the String class, i.e. you have to invoke it on the instance of the String. So, need to define this string either by outside the lambda or hard-code it inside the body of lambda.
public static final String str = "a";
Function<Integer, String> fun = num -> str.repeat(num);
Now, in order to transform this lambda expression in a method reference, let's first pose the question of what the method reference is.
Method reference - is the way to utilize an existing method to provide an implantation of the behavior defined by a function interface.
There are four kinds of method references (a quote from the Oracles's tutorial):
Reference to a static method ContainingClass::staticMethodName
Reference to an instance method of a particular object containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName
Reference to a constructor ClassName::new
Note that the syntax of method references doesn't require parentheses () because that how the language was designed, and I guess because method references are intended to be concise and expressive.
The type of method reference that we need to implement this function using repeat() method is a reference to an instance method of a particular object (because repeat() is an instance method). And that's how it might look like:
public static final String str = "a";
Function<Integer, String> fun = str::repeat;
print(3, fun); // or print(3, str::repeat);
Note that variables used inside both method references and lambda expressions must be final or effectively final, i.e. should be assigned only once.
I learnt that invokedynamic instruction of bytecode calls a static method representation of lambda.
Please let me know if that is incorrect.
if correct, then how is below code working?
String[] arr = new String[1];
Stream.of(arr).forEach((s) -> System.out.println(this));
It’s not correct to say that lambda expressions were always compiled to a static method. It’s not specified, how they are compiled, which leaves room for two different strategies for a lambda expression that captures this, like your s -> System.out.println(this).
use an instance method:
private void compiler$chosen$name(String s) {
System.out.println(this);
}
use a static method:
private static void compiler$chosen$name(TypeOfThis var0, String s) {
System.out.println(var0);
}
Both methods work equally well when the invokedynamic instruction points to a bootstrap method in the LambdaMetafactory. In either case, the invokedynamic instruction will have a signature consuming a TypeOfThis instance and producing a Consumer<String>. From the documentation of the LambdaMetafactory, you can derive that it will treat the receiver of a non-static target method like an implied the first argument, which makes the functional signature of both variants identical. All that matters, is that the argument to the consumer’s accept method has to correspond to the last argument of the list.
I’ve encountered both strategies in practice, so this is indeed compiler dependent.
Note that these strategies also work on source code level, when using method references:
public class Example {
BiConsumer<Example,String> variant1 = Example::instanceMethod;
BiConsumer<Example,String> variant2 = Example::staticMethod;
private void instanceMethod(String s) {
System.out.println(this);
}
private static void staticMethod(Example instance, String s) {
System.out.println(instance);
}
}
This demonstrates the equivalence of a method receiver and the the first argument to a static method. However, when it comes to binding an argument, only Consumer<String> c = this::instanceMethod; works with method references. The other binding features of the LambdaMetafactory are only used by compiler generated code for lambda expressions.
My code:
class BlogPost {
String title;
String author;
BlogPostType type;
int likes;
public BlogPost(String title, String author, BlogPostType type, int likes) {
this.title = title;
this.author = author;
this.type = type;
this.likes = likes;
}
//getter setter
}
and:
public enum BlogPostType {
NEWS,
REVIEW,
GUIDE
}
and:
public static void main(String[] args) {
List<BlogPost> posts = Arrays.asList(new BlogPost("Start Java", "Ram", BlogPostType.NEWS, 11),
new BlogPost("Start Java 8", "Rajou", BlogPostType.REVIEW, 101),
new BlogPost("Functional programming", "Das", BlogPostType.REVIEW, 111),
new BlogPost("Lambda", "Ramos", BlogPostType.GUIDE, 541));
Map<BlogPostType, List<BlogPost>> Blist = posts.stream().collect(groupingBy(BlogPost::getType));
System.out.println(Blist);
}}
I have three classes one is BlogPost , BlogPostType and Main.
I am making a map of Map<BlogPostType, List<BlogPost>> Blist by using groupingBy() and it works perfectly fine. i used a method reference there BlogPost::getType , i can use lambda expression also (x) -> x.getType().
But when i try to change the type of Map , i.e Map<String, List<BlogPost>> Blist1 then i cannot use Method reference. Is there any possible way to use method reference and get the type also changed??
I am thinking why cant we use like this: BlogPost::getType.toString() or (String)BlogPost::getType while we can do this in lambda (x) -> x.getType().toString().
Any possible ways to use Method reference and get along with conversion of type also?
you can use Function.identity() to chain method references (as many as you want). For example, put the following function in groupingBy:
Function.<BlogPost>identity()
.andThen(BlogPost::getType)
.andThen(BlogPostType::toString)
but it's better to use lambda
Method reference in place of lambda expression makes your code more
readable, hence it is advised to replace lambda expression with method
reference, Whenever Possible.
Remember a method reference replace a single method invocation , in your case BlogPost::getType will work fine while BlogPost::getType.toString() will not work as it is not single method invocation.
A method reference replace a single method invocation, so it can’t simply replace a lambda expression consisting of more than one method invocation.
You can do it with two method references as follows, but I'd stick with the lambda expression, which is much simpler.
Map<String, List<BlogPost>> Blist =
posts.stream()
.collect(Collectors.groupingBy(((Function<BlogPost,BlogPostType>)BlogPost::getType).andThen(BlogPostType::toString)));
or
Function<BlogPost,BlogPostType> getType = BlogPost::getType;
Map<String, List<BlogPost>> Blist =
posts.stream()
.collect(Collectors.groupingBy(getType.andThen(BlogPostType::toString)));
A method reference is just that: a "reference" to some specific method.
There is no implicit conversion or anything. Just a method that has a certain signature, and syntactic shortcut to express that without writing down a lambda expression.
If you want to use a method reference, that thing must exist as method. In other words, you would need to a new method like
String getTypeAsString()
to your BlogPost class. Only then you can go and directly invoke that method via a method reference.
But in your case, simply use that lambda expression instead that calls toString() explicitly. It sounds wrong to have a special method there that does "almost" the same as another method, just to enable you to write down a method reference.
Alternatively, follow the interesting approach pointed out in Eran's answer to use andThen().
In the end, your focus should be to write code that is easy to read and understand for you and your team. My personal recommendation would be to use the lambda right there, as all other solutions just add a lot of noise for no real gain.
When using map with method reference in Java, I met following problem:
public class Dummy {
public static void main(String[] args) {
IntegerHolder ih = new IntegerHolder();
Optional<IntegerHolder> iho = Optional.of(ih);
iho.map(IntegerHolder::getInteger).map(Objects::toString);
iho.map(IntegerHolder::getInteger).map((Integer ii) ->ii.toString());
iho.map(IntegerHolder::getInteger).map(Integer::toString);// this line will not compile. The error is "non-static method toString() cannot be referenced from a static context"
}
private static class IntegerHolder {
private Integer i;
Integer getInteger() {return i;}
}
}
It looks to me that Integer::toString is same as the IntegerHolder::getInteger. Both are "Reference to an Instance Method of an Arbitrary Object of a Particular Type"
I do not understand why one works, but the other does not.
Could you please shed some light on this question? Thank you very much.
The error is very misleading, in java-11 for example the error would make a lot more sense:
reference to toString is ambiguous
both method toString(int) in Integer and method toString() in Integer match)
If you re-write this method via a lambda expression, you will see that both signatures can potentially match:
iho.map(IntegerHolder::getInteger).map((Integer ii) -> Integer.toString(ii));
iho.map(IntegerHolder::getInteger).map((Integer ii) -> ii.toString());
both of these can be re-written as a method reference, but in such a case, which method to call?
I had some confusion about inner classes and lambda expression, and I tried to ask a question about that, but then another doubt arose, and It's probable better posting another question than commenting the previous one.
Straight to the point: I know (thank you Jon) that something like this won't compile
public class Main {
public static void main(String[] args) {
One one = new One();
F f = new F(){ //1
public void foo(){one.bar();} //compilation error
};
one = new One();
}
}
class One { void bar() {} }
interface F { void foo(); }
due to how Java manages closures, because one is not [effectively] final and so on.
But then, how come is this allowed?
public class Main {
public static void main(String[] args) {
One one = new One();
F f = one::bar; //2
one = new One();
}
}
class One { void bar() {} }
interface F { void foo(); }
Is not //2 equivalent to //1? Am I not, in the second case, facing the risks of "working with an out-of-date variable"?
I mean, in the latter case, after one = new One(); is executed f still have an out of date copy of one (i.e. references the old object). Isn't this the kind of ambiguity we're trying to avoid?
A method reference is not a lambda expression, although they can be used in the same way. I think that is what is causing the confusion. Below is a simplification of how Java works, it is not how it really works, but it is close enough.
Say we have a lambda expression:
Runnable f = () -> one.bar();
This is the equivalent of an anonymous class that implements Runnable:
Runnable f = new Runnable() {
public void run() {
one.bar();
}
}
Here the same rules apply as for an anonymous class (or method local class). This means that one needs to effectively final for it to work.
On the other hand the method handle:
Runnable f = one::bar;
Is more like:
Runnable f = new MethodHandle(one, one.getClass().getMethod("bar"));
With MethodHandle being:
public class MethodHandle implements Runnable {
private final Object object;
private final Method method;
public MethodHandle(Object object, java.lang.reflect.Method method) {
this.object = Object;
this.method = method;
}
#Override
public void run() {
method.invoke(object);
}
}
In this case, the object assigned to one is assigned as part of the method handle created, so one itself doesn't need to be effectively final for this to work.
Your second example is simply not a lambda expression. It's a method reference. In this particular case, it chooses a method from a particular object, which is currently referenced by the variable one. But the reference is to the object, not to the variable one.
This is the same as the classical Java case:
One one = new One();
One two = one;
one = new One();
two.bar();
So what if one changed? two references the object that one used to be, and can access its method.
Your first example, on the other hand, is an anonymous class, which is a classical Java structure that can refer to local variables around it. The code refers to the actual variable one, not the object to which it refers. This is restricted for the reasons that Jon mentioned in the answer you referred to. Note that the change in Java 8 is merely that the variable has to be effectively final. That is, it still can't be changed after initialization. The compiler simply became sophisticated enough to determine which cases will not be confusing even when the final modifier is not explicitly used.
The consensus appears to be that this is because when you do it using an anonymous class, one refers to a variable, whereas when you do it using a method reference, the value of one is captured when the method handle is created. In fact, I think that in both cases one is a value rather than a variable. Let's consider anonymous classes, lambda expressions and method references in a bit more detail.
Anonymous classes
Consider the following example:
static Supplier<String> getStringSupplier() {
final Object o = new Object();
return new Supplier<String>() {
#Override
public String get() {
return o.toString();
}
};
}
public static void main(String[] args) {
Supplier<String> supplier = getStringSupplier();
System.out.println(supplier.get()); // Use o after the getStringSupplier method returned.
}
In this example, we are calling toString on o after the method getStringSupplier has returned, so when it appears in the get method, o cannot refer to a local variable of the getStringSupplier method. In fact it is essentially equivalent to this:
static Supplier<String> getStringSupplier() {
final Object o = new Object();
return new StringSupplier(o);
}
private static class StringSupplier implements Supplier<String> {
private final Object o;
StringSupplier(Object o) {
this.o = o;
}
#Override
public String get() {
return o.toString();
}
}
Anonymous classes make it look as if you are using local variables, when in fact the values of these variables are captured.
In contrast to this, if a method of an anonymous class references the fields of the enclosing instance, the values of these fields are not captured, and the instance of the anonymous class does not hold references to them; instead the anonymous class holds a reference to the enclosing instance and can access its fields (either directly or via synthetic accessors, depending on the visibility). One advantage is that an extra reference to just one object, rather than several, is required.
Lambda expressions
Lambda expressions also close over values, not variables. The reason given by Brian Goetz here is that
idioms like this:
int sum = 0;
list.forEach(e -> { sum += e.size(); }); // ERROR
are fundamentally serial; it is quite difficult to write lambda bodies
like this that do not have race conditions. Unless we are willing to
enforce -- preferably at compile time -- that such a function cannot
escape its capturing thread, this feature may well cause more trouble
than it solves.
Method references
The fact that method references capture the value of the variable when the method handle is created is easy to check.
For example, the following code prints "a" twice:
String s = "a";
Supplier<String> supplier = s::toString;
System.out.println(supplier.get());
s = "b";
System.out.println(supplier.get());
Summary
So in summary, lambda expressions and method references close over values, not variables. Anonymous classes also close over values in the case of local variables. In the case of fields, the situation is more complicated, but the behaviour is essentially the same as capturing the values because the fields must be effectively final.
In view of this, the question is, why do the rules that apply to anonymous classes and lambda expressions not apply to method references, i.e. why are you allowed to write o::toString when o is not effectively final? I do not know the answer to that, but it does seem to me to be an inconsistency. I guess it's because you can't do as much harm with a method reference; examples like the one quoted above for lambda expressions do not apply.
No. In your first example you define the implementation of F inline and try to access the instance variable one.
In the second example you basically define your lambda expression to be the call of bar() on the object one.
Now this might be a bit confusing. The benefit of this notation is that you can define a method (most of the time it is a static method or in a static context) once and then reference the same method from various lambda expressions:
msg -> System.out::println(msg);