Change Type of Method Reference - java

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.

Related

How can a parameterless method toUpperCase() implement Function.apply()? [duplicate]

This question already has answers here:
How does method reference casting work?
(3 answers)
Closed last month.
This post was edited and submitted for review last month and failed to reopen the post:
Original close reason(s) were not resolved
I'm learning Java 8 with Lambda, Streams, and method reference. Regarding the example below,
Optional<String> s = Optional.of("test");
System.out.println(s.map(String::toUpperCase).get());
I don't understand how is it possible to use String::toUpperCase as an input for this map() method.
This is the method implementation:
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
return Optional.ofNullable(mapper.apply(value));
}
}
So it requires a function interface, and it has this apply() method: R apply(T t); This method has an input argument.
And toUpperCase() method doesn't have any argument:
public String toUpperCase() {
return toUpperCase(Locale.getDefault());
}
If the abstract method apply(T t) has one argument, then the implemented method should have one argument of the same type. How can parameterless method toUpperCase() implement the apply(T t) method from a function interface?
I try to recreate the same conditions:
I create a functional interface:
public interface Interf {
String m1(String value);
}
Then I create a class with the method reference for m1():
public class Impl {
public String value;
public String toUpp() {
return value.toUpperCase();
}
}
And here is a class for test:
public class Test {
public static void main(String[] args) {
Interf i = String::toUpperCase;
System.out.println(i.m1("hey"));
Interf i1 = Impl::toUpp;
System.out.println(i.m1("hello"));
}
}
There isn't any issue at this statement: Interf i = String::toUpperCase; but there is a compilation error on this line: Interf i1 = Impl::toUpp;. It says:
Non-static method cannot be referenced from a static context
But toUpperCase() is also a non-static method. And even if I make the toUpp() static, it is still not working, it is working only if I add a String argument as an input argument for toUpp(). But then why is it working for String::toUpperCase?
TL;DR
Parameters that a Method reference is expected to consume according to the contract imposed by a Functional interface it implements are NOT necessarily the same as parameters of the method used in the Method reference.
This answer is a journey from this common misconception towards understanding all syntactical flavors of Method references.
Let's take tiny baby steps to dispel misunderstanding, starting from the definition of the Method reference.
What is Method reference
Here is the definition of the Method reference according to the Java Language Specification §15.13. Method Reference Expressions
A method reference expression is used to refer to the invocation of a
method without actually performing the invocation. Certain forms of
method reference expression also allow class instance creation (§15.9)
or array creation (§15.10) to be treated as if it were a method
invocation.
emphasis added
So, a Method reference is a mean of referring the method-invocation without invoking a method. Or in other words, it's a way of describing a certain behavior by delegating to the existing functionality.
Let's take a small detour and have a look at the sibling of a Method reference, a Lambda expression.
Lambda expressions are also a way to describe behavior (without executing it), and both lambdas and Method references should conform to a Functional interface. Let's declare some lambdas.
Consider, we have a domain class Foo and utility class FooUtils.
public class FooUtils {
public static Foo doSomethingWithFoo(Foo foo) {
// do something
return new Foo();
}
}
And we need to define a function of type UnaryOperator<Foo>, let's start with writing a lambda expression:
UnaryOperator<Foo> fooChanger = foo -> FooUtils.doSomethingWithFoo(foo);
Lambda receives an instance of Foo as an argument and feeds it into the existing utility method. Quite simple, right? Nothing special happens inside the lambda's body, and since have defined the type as UnaryOperator<Foo> the lambda should expect Foo. Everything is absolutely predictable, isn't it? Now the question is: can we alternate that?
Sure, we can!
UnaryOperator<Foo> fooChanger = FooUtils::doSomethingWithFoo;
That's where a Method reference comes to the rescue. It provides a shortened syntax by:
1. Dropping the lambda's arguments (they are still there, we're simply not displaying them because we know what they are).
2. Removing the parentheses after the method name. Again the method declaration is known (and let's assume that there are no ambiguities), and we are not performing any prior transformation with arguments, and we are not using any additional arguments apart from those that should come according to the contract of the Functional interface. Only in this case everything is predictable and can a method reference.
Key takeaways:
you may think of method references as if they are shortened lambdas.
the arguments of the method reference are the same the equivalent lambda receives because they are implementations to the same interface. These parameters are still there implicitly, just dropped for the purpose of conciseness. And more importantly, parameters that a method reference consumes should not be confused with parameters expected method it refers to. In other word the first parameters an input of the method reference (and they are compliant with the contract defined by the interface) and the latter related to what happens inside the reference, and have no connection to the first ones.
More examples
Let's examine a few more examples. Let's say we have a simple object representing a coin with a single property isHeads describing which side the coin is showing (i.e. heads or tails).
public static class Coin {
public static final Random RAND = new Random();
private final boolean isHeads;
public Coin() {
this.isHeads = RAND.nextBoolean();
}
private Coin(boolean isHeads) {
this.isHeads = isHeads;
}
public Coin reverse() {
return new Coin(!isHeads);
}
public boolean isHeads() {
return isHeads;
}
}
Let's generate a coin. For that we can use implement of Supplier which very generous, supplier doesn't receive arguments, it produces a value. Let's definable both a lambda and a reference
Supplier<Coin> coinProducer = () -> new Coin(); // no argument required according to the contract of Supplier
Supplier<Coin> coinProducer1 = Coin::new; // Supplier expressed as a reference to no-args constructor
Both don't receive any argument (as per contract of the Supplier), both refer to the no-arguments constructor.
Now let's consider the predicates determining if the coin shows heads implemented via a lambda and a method reference:
Predicate<Coin> isHeads = coin -> coin.isHeads();
Predicate<Coin> isHeads1 = Coin::isHeads;
Again, both the lambda and the method reference are compliant with the Predicate's contract and both receive an instance of Coin as an argument (it can't be otherwise, simply concise syntax of the method reference doesn't show that).
So far, so good? Let's move further and try another way to obtain a Coin, let's define a Function:
Function<Boolean, Coin> booleanToCoin = value -> new Coin(value);
Function<Boolean, Coin> booleanToCoin1 = Coin::new;
Now both the lambda and the reference are consuming a boolean value and making use of the parameterized constructor. Did not notice that method reference describing Supplier<Coin> and Function<Boolean, Coin> looks identical.
Reminder: both Lambda expressions and Method references have no type by itself. They are so-called poly-expressions, which means their type should be inferred by the compiler based on the context in which they appear. Both the lambda and the reference should conform to a Functional interface, and the interface they implement dictates who they are and what they are doing.
In all examples described earlier, arguments of consumed by a method reference appeared to be the same as the ones expected by the referenced method, but it's not mandatory for them to be the identical. It's time to examine a couple or examples where it not the case to dispel the illusions.
Let's consider a UnaryOperator reversing a coin:
UnaryOperator<Coin> coinFlipper = coin -> coin.reverse(); // UnaryOperator requires one argument
UnaryOperator<Coin> coinFlipper1 = Coin::reverse; // UnaryOperator still requires one argument expressed as a reference to no arg method
All implementations of the UnaryOperator receive a Coin instance as an argument, and another coin is being produced as a result of the invocation of reverse(). The fact that reverse is parameterless is not an issue, because we concerned about what it produces, and not what it consumes.
Let's try to define a tougher method reference. To begin with, introduce in the Coin class a new instance method called xor(), which is immensely useful for XOR-ing two coins:
public Coin xor(Coin other) {
return new Coin(isHeads ^ other.isHeads);
}
Now when two object come into play we have more possibilities, let's start with the simplest case one by defining a UnariOperator:
final Coin baseCoin = new Coin();
UnaryOperator<Coin> xorAgainstBase = coin -> baseCoin.xor(coin);
UnaryOperator<Coin> xorAgainstBase1 = baseCoin::xor;
In the above example an instance of Coin defined outside the function is used to perform the transformation via the instance-method.
A little bit more complicated case would be a BinaryOperator for XOR-ing a couple of coins might look like this:
BinaryOperator<Coin> xor = (coin1, coin2) -> coin1.xor(coin2);
BinaryOperator<Coin> xor1 = Coin::xor;
Now we have two arguments coming as an input and a Coin instance should be produce as an output as per BinaryOperators contract.
The interesting thing is the first argument serves as an instance on which the method xor() would be invoked, and the second is passed to the method (note that xor() expect only one argument).
You might ask what would happen if there would be another method for XOR-ing coins. A static method expecting two arguments:
public static Coin xor(Coin coin1, Coin coin2) {
return new Coin(coin1.isHeads ^ coin2.isHeads);
}
Then the compiler would fail to resolve the method reference, because here we have more the one potentially applicable method and none of them can be considered to be more specific than the other since the types of arguments are the same. That would cause a compilation error. But if we would have either of them (not both together), reference Coin::xor would work fine.
Types of Method references
Basically, the examples that we have walked through covered all the types of method references. Now, let's enumerate them.
The official tutorial provided by Oracle re are four kinds of method references:
Reference to a Static method
Class::staticMethod
Example Coin::xor which refers to the static method xor(Coin coin1, Coin coin2).
Examples with standard JDK-classes:
BinaryOperator<Integer> sum = Integer::sum; // (i1, i2) -> Integer.sum(i1, i2)
BiFunction<CharSequence, Iterable<CharSequence>, String> iterableToString
= String::join; // (delimiter, strings) -> String.join(delimiter, strings)
Reference to an instance method of a particular object
instance::instanceMethod
The example illustrating this case would the usage of the instance method xor(Coin other) with a coin defined outside the function, which is internaly used to invoke xor() on it passing the function-argument into the method.
final Coin baseCoin = new Coin();
UnaryOperator<Coin> xorAgainstBase1 = baseCoin::xor; // same as coin -> baseCoin.xor(coin)
Examples with standard JDK-classes:
Set<Foo> fooSet = // initialing the Set
Predicate<Foo> isPresentInFooSet = fooSet::contains;
Reference to an Instance Method of an Arbitrary Object of a Particular Type
Class::methodName
In this case method refernce operates on an instance that comes as an argument (we would have reference to it only it we would use a lambda), therefore containing type, which can be tha actual type or one the super types, is used to refer to this instance.
An example would a Predicate checking if the coin shows heads Coin::isHeads.
Examples with standard JDK-classes:
Function<List<String>, Stream<String>> toStream = Collection::stream;
List<List<String>> lists = List.of(List.of("a", "b", "c"), List.of("x", "y", "z"));
List<String> strings1 = lists.stream()
.flatMap(toStream)
.toList();
// A slightly more complicate example taken from the linked tutorial
// Equivalent lambda: (a, b) -> a.compareToIgnoreCase(b)
String[] stringArray = { "Barbara", "James", "Mary" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
Reference to a Constructor
Class::new
We have cove this case already with the following examples:
Supplier<Coin> refering to no args-constracor implemented as Coin::new;
Function<Boolean, Coin> which makes use of the single-arg constructor by passing incoming boolean value also expressed as Coin::new.
How can toUpperCase() method implement apply(T t) method from Function
interface?
The method reference String::toUpperCase has unbound receiver.
Java 8: Difference between method reference Bound Receiver and UnBound Receiver
Then the argument T t would be the receiver
In your example
public <U, T> Optional<U> map(Function<? super T, ? extends U> mapper) {
...
return Optional.ofNullable(mapper.apply(value));
}
by calling map(String::toUpperCase)
If value, let's say equal to "Example String", would be the receiver so mapper.apply("Example String"); would be equivalent to "Example String".toUpperCase();

How can I use Java 11 repeat() method in a Method reference?

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.

Local classes with Lambda expressions

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.

Pass method as parameter for another method

I want to do something like this:
public class Myclass() {
ArrayList<Object> objects;
public Myclass() {
this.objects = new ArrayList<>();
}
public ArrayList<Object> callObjectAndMethod(Method method, String string) {
ArrayList<Object> returnlist = new ArrayList<>();
// call method for given object reference. I also assume that return value of the method is String
String output = object.method();
for (Object object : this.objects) {
if (object.method.contains(string)) {
returnlist.add(object.method());
}
return returnlist;
}
}
Is it possible to pass method as parameter for another method ? Also i would like to do this sort of iteration for multiple variable types. int,String,double is there way to create method that works with multiple input types. (probably not) but i am only trying to reduce the amount of lines i have to write. i will have a lot to write if i write this thing for all the methods i want to compare or use copy-paste technique and change little things (which is not good).
You could use java-8 functional Programming. It allows using Function Interfaces to create such implementations called lambda expressions. These lambda expressions could be passed as variables to other methods. For example
Predicate isLengthTen = string -> string.length() == 10;
Now this could be passed as a parameter to any method. Hence, you could use java 8 functional programming easily to pass the code as parameter basically.
boolean testTruth(Predicate<T> predicate,T dataUnderTest){
predicate.test(dataUnderTest);
}
Hope it helps!
Quick read about how to use is here

SONAR: Replace this lambda with a method reference

Sonar tells me "Replace this lambda with a method reference"
public class MyClass {
private List<SomeValue> createSomeValues(List<Anything> anyList) {
return anyList //
.stream() //
.map(anything -> createSomeValue(anything)) //
.collect(Collectors.toList());
}
private SomeValue createSomeValue(Anything anything) {
StatusId statusId = statusId.fromId(anything.getStatus().getStatusId());
return new SomeValue(anything.getExternId(), statusId);
}
}
Is this possible here? I tried several things, like
.map(MyClass::createSomeValue) //
but I need to change the method to static then. And I am not a big fan of static methods.
Explanation of SonarQube is:
Method/constructor references are more compact and readable than using lambdas, and are therefore preferred.
Yes, you can use this::createSomeValue:
private List<SomeValue> createSomeValues(List<Anything> anyList) {
return anyList //
.stream() //
.map(this::createSomeValue) //
.collect(Collectors.toList());
}
This kind of method reference is called "Reference to an instance method of a particular object". In this case, you are referring to the method createSomeValue of the instance this.
Whether it is "better" or not that using a lambda expression is a matter of opinion. However, you can refer to this answer written by Brian Goetz that explains why method-references were added in the language in the first place.

Categories

Resources