Can you explain why the first unwrapped method reference does not compile? - java

In this example, passing a method reference to Stream.of does not work but once it's wrapped it works. I can't quite get my head around why this is. Isn't the method reference equivalent to the functional interface?
public class A {
String a() {
return "";
}
void b() {
Stream.of(this::a); // won't compile
Stream.of(wrap(this::a)); // will compile
}
static <T> Supplier<T> wrap(Supplier<T> f) {
return f;
}
}

Stream.of has the following signature:
public static<T> Stream<T> of(T t)
The following example will compile because you explicitly providing a type information for T.
Stream<Supplier<String>> a = Stream.of(this::a);
The first example Stream.of(this::a); equivalent to:
Object a = this::a;
where Object is not a functional interface and will not compile.
Providing with a functional interface this example compiles:
Runnable a = this::a;
Stream.of(a);
In the second example, wrap provides a functional interface Supplier

Stream.of(T) expects an Object and you pass to it a method reference in the first statement. But an Object parameter is not a functional interface, so it cannot accept a method reference or a lambda that is not specifically typed.
With lambda, it would produce also an error : Stream.of(()->this.a()).
A simpler example could be Stream.of(()-> "foo") that will just not compile.
But if you type the method reference or the lambda it works :
Stream.of((Supplier<String>) this::a)
or
Stream.of((Supplier<String>) () -> this.a())
In the working statement you pass to Stream.of(T) a parameter that is a Supplier<String>. That refers to a functional interface but that is typed as in the previous working examples, so it is valid as parameter that expects an Object.

this::a is contextless and could mean different things. You need to provide some context to help the compiler to figure out what you actually meant by this::a.
Stream.<Supplier<String>>of(this::a);
Though, that Stream<Supplier<String>> doesn't seem to be what you wanted. If you need a Stream<String>, use Stream.generate: no extra type information needed since the method takes a Supplier<T> (no ambiguity here).
Stream.generate(this::a);
On a side note, both statements expect you to save their results into variables. Defining variables of the right type often facilitates resolving such issues.
Stream<Supplier<String>> s1 = Stream.of(this::a);
Stream<String> s2 = Stream.generate(this::a);
All credit to #J-Alex and #Holger for their precious comments.

Related

'java.util.ArrayList' cannot be applied to '(<lambda parameter>)'

How can I fix this problem?
rv_groupAddMember.setAdapter(new GroupAdapter(groupModelList, position -> {
selectedGroup = groupModelList.get(position);
addMember_groupName.setText("Seçili Grup :"+selectedGroup.getGroupName());
}));
public class GroupAdapter extends RecyclerView.Adapter<GroupAdapter.GroupViewHolder>{
List<GroupModel> groupModelList;
OnClickItem onClickItem;
public GroupAdapter(List<GroupModel> groupModelList, OnClickItem onClickItem) {
this.groupModelList = groupModelList;
this.onClickItem = onClickItem;
}
I added my GroupAdapter class and constructor
error
I'm watching tutorial everything is the same and there shouldn't be an error.
The problem is, java needs to figure out what the lambda is actually trying to be, and only then can it know what the type of position is. For example, given:
void foo(Consumer<String> x) { ... }
If you then write: foo(s -> System.out.println(s)); - java knows that the type of s is String only because of this sequence of events:
There is only one foo method.
That method takes a param of type Consumer<String>.
That type is a functional interface (an interface with a single method), and that method's signature is void accept(String t).
The lambda is an implementation of that method, therefore, s must be String.
And only then can java continue to actually understand what the lambda contains.
Your error is telling you in a somewhat odd way that the compiler is not currently capable of figuring out what the functional interface is that this lambda is implementing. Therefore, it does not know what the type of position is, therefore, the call groupModelList.get(position) does not work; int is required, but the type of position is 'I do not know yet; I cannot know until I know what functional interface this lambda is trying to implementation and for some reason I can't know that right now'. Which isn't int, so you get that error.
one trivial fix is to be explicit. Make that (int position) -> { ... } instead. This may then lead you to the real error you're interested in. For example, if you haven't imported GroupAdapter, this would happen.

Is it possible to use method reference with static method of interface?

This is my first code:
public class MethodReference {
public static void main (String []args) {
Stream<String> s = Stream.of("brown bear", "grizzly");
s.sorted(Comparator.reverseOrder()).forEach(System.out::print);
//...
}
}
Result: grizzlybrown bear
This is my second code:
public class MethodReference {
public static void main (String []args) {
Stream<String> s = Stream.of("brown bear", "grizzly");
s.sorted(Comparator::reverseOrder()).forEach(System.out::print);
//...
}
}
Result: compiler error
My questions:
Why is there a compiler error in the second code?
Can't I use the method reference for static method of functional interface?
I know I can't use method reference with default method of functional interface.
I know I can use method reference with a class in 5 cases:
Class
Class::staticMethod
Class::instanceMethod
instance::instanceMethod
Class::new
Functional Interface
Interface::abstractMethod
Thanks a lot!
Comparator.reverseOrder() is an expression which resolves to the Comparator type, because that's what it returns.
Comparator::reverseOrder is an expression which resolves to a method which takes no arguments and returns a Comparator e.g. a Supplier<Comparator<String>>, though it could be any matching functional interface.
In the second instance you are trying to pass a method (which provides a Comparator) as an argument. The method doesn't want that - it just wants the Comparator itself.
You could think of it like this (just pseudo-code to demonstrate the point):
s.sorted(new Comparator())
vs
s.sorted(new Supplier(new Comparator()))
To answer your second question as to whether it's ever possible to use a method reference for a static method of an interface - yes, absolutely!
If we declare the following method:
<T> void giveMeAComparatorSupplier(Supplier<Comparator<T>> supplier) { }
then we can definitely call it with a method reference
giveMeAComparatorSupplier(Comparator::reverseOrder);
(And FYI your method reference syntax is wrong - it never uses ())
Two things are wrong with your second code. First, method references do not use parentheses or arguments at all. You would need to supply only the method that would be called later; you are not calling the method at that point.
Second, the sorted method takes a Comparator, not a functional interface that would supply a Comparator. The method needs a Comparator already created and ready to go, not a functional interface that will supply a Comparator when needed.
It has nothing to do with the fact that Comparator is an interface; one can generally create a method reference to a static interface method. It has everything to do with the fact that sorted needs an actual Comparator instance and not an instance of a functional interface, which is when you could supply a method reference.
So even if you take off the parentheses, it still won't compile. Only your first code, which directly passes a Comparator, will compile and work as expected.

Functional Interface Object Cast

I've encountered the following code in a Java project, and I'm not sure what to make of it:
public Function<CustomEnum,String> foo(SomeObject someObject) {
return ((Function<CustomEnum,String>) (someObject::toString)).andThen(r -> someObject::getSomethingWithEnumParameter);
}
I don't really understand how you can cast something to a Functional Interface. What does that even mean?
Isn't the resulting type of the return value going to be whatever value someObject.
Isn't Function<CustomEnum, String> defining an anonymous function that takes a type CustomEnum and returns a String?
I've read the java doc for Function<T,R>, and to be honest, this doesn't make much more sense than before I read the document.
This is what I believe is happening.
foo is returning an anonymous function that is applied to some CustomEnum to return a String
the anonymous function inside of foo (which is somehow cast onto someObject::toString, which I don't understand) is applied to the CustomEnum that will be passed from the initial call of foo(someObject).apply(customEnum).
The andThen will take the resulting String from the anonymous function inside of foo (which was cast somehow I still don't understand), and then return the value of someObject::getSomethingWithEnumParameter. Why isn't the return type just the type of someObject::getSomethingWithEnumParameter, which we'll say is a Map<R,T>, for sake of discussion.
If anyone could help me understand this flow, I would greatly appreciate it.
In an ideal world this would work:
public Function<CustomEnum,String> foo(SomeObject someObject) {
return (someObject::toString).andThen(...);
}
However Java needs an interface type in order to implicitly create an interface instance from the method reference., hence the explicit cast is required to cast to the Function interface type.
Once you have an instance of Function then you can call any method on it as per normal, in this case the andThen method which composes it with another function object to form a new function.
Breaking it down:
someObject::toString is a method reference with implied type Function<CustomEnum, String>. I.e. toString is a method on SomeObject which takes a parameter of type CustomEnum and returns a String.
r -> someObject::getSomethingWithEnumParameter has the wrong type though - it's a function which returns a function. If you get rid of the "r ->" part then it is valid, as long as someObject::getSomethingWithEnumParameter is a method on SomeObject that takes a String and returns a String. Alternatively the return type of foo would need to change to Function<CustomEnum, Function<String, String>>.
If you combine those two with andThen then you have a Function which takes a CustomEnum and returns a String, as pr the return type of foo.

Cannot access class methods via generics

I changed my method to generic method. What is happening now is that I was deserializing the class inside the methodB and accessing its methods which I can not do anymore.
<T> void methodB(Class<T> clazz) {
T var;
HashMap<String, T> hash = new HashMap<>();
}
void methodA () {
methodB(classA.class);
}
Initially inside methodB with no generics,
var = mapper.convertValue(iter.next(), ClassA.class);
var.blah() //works fine
After using generics,
var = mapper.convertValue(iter.next(), clazz);
var.blah() //cannot resolve the method.
How do I access blah() method of classA?
I think you should use interfaces instead of generics, if you want to call the same 'blah' function on a variety of classes (A,X,Y,Z) (each of which has the same function signature)..
Your other option (if you cannot modify A, e.t.c) is to use reflection. read more about this in https://docs.oracle.com/javase/tutorial/reflect/
Thanks to Passing a class with type parameter as type parameter for generic method in Java. Solved using TypeToken
The line where you assign to var at runtime is absolutely irrelevant. The only thing that matters for compiling a call is the static (compile-time) type of var. Since T is unbounded (other than by Object), it is not known to support any methods, other than those provided by Object. Both pieces of code should fail to compile.

How to pass a constructor reference for parameterised lambda?

I have a method looks like this.
static void doSomething(Function<Irrelevant, Some> supplier) {
}
When I try to do this,
doSomething((i) -> new Some()); // i is irrelevant
it works. But when I try to do this,
doSomething((i) -> Some::new); // compiler -> bad return type in lambda expression
it doesn't.
Is this normal? Is there any way that I can pass a constructor reference for a parameterised function?
A constructor with no parameters would be a Supplier<Some> and thus it is incompatible with a method asking for a Function<Irrelevant, Some>.
Your lambda in doSomething((i) -> Some::new) would actually take some element and return a reference to that constructor, i.e. a Supplier<Some>, not that Some itself. You were probably looking for doSomething(Some::new) -- but as I said, this will not work either since the constructor does not match the method's parameter (it would work, if the constructor had a parameter of type Irrelevant).
If the parameter is really irrelevant, then you could change the method's signature to
static void doSomething(Supplier<Some> supplier) {
If that's not possible, your approach with doSomething((i) -> new Some()); is perfectly fine.

Categories

Resources