When do method references work? [duplicate] - java-8

This question already has answers here:
How to fix ambiguous type on method reference (toString of an Integer)?
(3 answers)
Closed 5 years ago.
Method references don't work with non-static methods AFAIK. I tried using them in the following way
Arrays.stream(new Integer[] {12,321,312}).map(Integer::toString).forEach(System.out::println);
Which resulted in the compilation error as seen in the link.
Problem
While using AssertJ library, I used something like this,
AbstractObjectAssert<?, Feed> abstractObjectAssertFeed2 = assertThat(feedList.get(2));
abstractObjectAssertFeed2.extracting(Feed::getText).isEqualTo(new Object[] {Constants.WISH+" HappyLife"});
where Feed is a noun and getText is a getter method and not static, but it worked fine without compilation error or any error which puzzled me.
Am I missing something about how method references work?

This is invalid for a different reason.
Basically there are two toString implementations in Integer.
static toString(int)
and
/*non- static*/ toString()
Meaning you could write your stream like this:
Arrays.stream(new Integer[] { 12, 321, 312 })
.map(i -> i.toString(i))
.forEach(System.out::println);
Arrays.stream(new Integer[] { 12, 321, 312 })
.map(i -> i.toString())
.forEach(System.out::println);
Both of these qualify as a method reference via Integer::toString. The first one is a method reference to a static method. And the second is Reference to an instance method of an arbitrary object of a particular type.
Since they both qualify the compiler does not know which to choose.

Related

emulating method type parameters in generic go [duplicate]

This question already has answers here:
How can I emulate `fmap` in Go?
(2 answers)
Closed 8 months ago.
After watching Philip Wadler's talk on featherweight go I was really excited about the newest go generics draft. But now with a version of the new generics draft available for us to play with it seems some of the things from featherweight go are no longer possible. In the both the talk, and the paper he introduces a functor like interface called List. The approach from the paper doesn't quite work.
type Any interface {}
type Function(type a Any, b Any) interface {
Apply(x a) b
}
type Functor interface {
Map(f Function) Functor
}
fails with the error: cannot use generic type Function(type a, b) without instantiation
and if you try to add type parameters to the method, and use a normal function you get: methods cannot have type parameters
I'm wonder if anyone has found an approach to making functors work with the current version of the draft.
You haven't carried the generic types through; in your example code, Functor is treating Function as if it were not generic. The correct code (which compiles, see here) would be:
type Function(type a Any, b Any) interface {
Apply(x a) b
}
type Functor(type a Any, b Any) interface {
Map(f Function(a,b)) Functor(a,b)
}

Method Reference - passing Function to method with Consumer argument

I'm learning about Method References from Java 8 and I have difficulties understanding why does this work?
class Holder {
private String holded;
public Holder(String holded) {
this.holded = holded;
}
public String getHolded() {
return holded;
}
}
private void run() {
Function<Holder, String> getHolded = Holder::getHolded;
consume(Holder::getHolded); //This is correct...
consume(getHolded); //...but this is not
}
private void consume(Consumer<Holder> consumer) {
consumer.accept(null);
}
As you can see in run method - Holder::getHolded returns unbound method reference which you can invoke by passing object of type Holder as an argument. Like this: getHolded.apply(holder)
But why it casts this unbound method reference to Consumer when it is invoked directly as an method argument, and it does not doing it when I'm passing Function explicitly?
Two things here, lambda expressions are poly expressions - they are inferred by the compiler using their context (like generics for example).
When you declare consume(Holder::getHolded);, compiler (under the so-called special void compatibility rule) will infer it to Consumer<Holder>.
And this might not look obvious, but think of a simplified example. It is generally more than ok do call a method and discard it's return type, right? For example:
List<Integer> list = new ArrayList<>();
list.add(1);
Even if list.add(1) returns a boolean, we don't care about it.
Thus your example that works can be simplified to:
consume(x -> {
x.getHolded(); // ignore the result here
return;
});
So these are both possible and valid declarations:
Consumer<Holder> consumer = Holder::getHolded;
Function<Holder, String> function = Holder::getHolded;
But in this case we are explicitly telling what type is Holder::getHolded,, it's not the compiler inferring, thus consume(getHolded); fails, a Consumer != Function after all.
Java 8 introduced 4 important "function shapes" in the package java.util.function.
Consumer -> accepts a method reference (or a lambda expression) that takes one argument but doesn't return anything
Supplier -> accepts a method reference (or a lambda expression) that takes no argument and returns an object.
Function -> accepts a method reference (or a lambda expression) that takes one argument and returns an object.
Predicate -> accepts a method reference (or a lambda expression) that takes one argument and returns a boolean.
Read the Java docs for more detail.
To answer your question on why the first one works but the second one errors out, read following:
The second statement
consume(getHolded);
doesn't work because the type of the argument getHolded is Function<Holder, String> whereas the consume method expects an argument of type Consumer<Holder>. Since there is no parent-child relationship between Function and Consumer, it requires an explicit cast without which the compiler rightly errors out.
The first statement
consume(Holder::getHolded);
works because the method getHolded is declared as public String getHolded() meaning that it doesn't take any argument and returns a String. As per the new void compatibility rule, void types are inferred as the class containing the referenced method. Consider the following statement:
Consumer<Holder> consumer = Holder::getHolded;
This is a valid statement even though the method getHolded doesn't accept any arguments. This is allowed to facilitate inferring void types. Yet another example is the one you have mentioned yourself:
Function<Holder, String> getHolded = Holder::getHolded;
This is also a valid statement where you have said that the function object getHolded is a Function that returns String and accepts a type Holder even though the assigned method reference doesn't take any argument.
Sharing just a summary of the four types of Method References under the hood:
Reference to a static method:
Type::staticMethod ===>>> x -> Type.staticMethod(x)
Reference to an instance method of a particular object:
instance::instanceMethod ===>>> x -> instance.instanceMethod(x)
Reference to an Instance Method of an Arbitrary Object of a Particular Type:
Type::instanceMethod ===>>> x -> x.instanceMethod() OR (x, y) -> x.instanceMethod(y)
Reference to a constructor:
Type::new ===> x -> new Type(x)

Reference to an instance method of a particular object breaks the type-safety in Java?

Does the notion of a reference to an instance method of a particular object break the type-safety in Java?
According to
https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
you can have a custom class ComparisonProvider that DOES not implement the Comparator interface, and still use an instance of this class as the second argument of the method
Arrays.sort(T[] a, Comparator c)
Sure, the implementation of your ComparisonProvider MUST have a method whose signature exactly matches the Comparator.compare() method, but that is still not an instance of Comparator, isn't it?
In essence, Java 8 allows us to use instances of classes as if they were implementing a particular interface, while actually they are not.
This means, that we are loosing Type-safety in Java, do we?
lambda expressions and method reference don't have a predefined type, they are poly expressions, as seen here. That means that their type is derived from the context in which they are used.
In your example these both would be legal for example:
BiFunction<Person, Person, Integer> biFun = myComparisonProvider::compareByName;
Comparator<Person> comp = myComparisonProvider::compareByName;
But at the same time you can't do:
Arrays.sort(pers, biFun);
When you actually try to sort the array like this:
Arrays.sort(pers, myComparisonProvider::compareByName);
At the bytecode level that is a Comparator:
// InvokeDynamic #0:compare:(LTest$ComparisonProvider;)Ljava/util/Comparator;
Also notice that this would print true:
Comparator<Person> comp = myComparisonProvider::compareByName;
System.out.println(comp instanceof Comparator); // true
You can enable a flag : -Djdk.internal.lambda.dumpProxyClasses=/Your/Path/Here
and look at what that method reference is transformed into:
final class Test$$Lambda$1 implements java.util.Comparator
and inside it there's the compare method implementation(I've simplified it and removed some of it's code to make it a little more obvious):
public int compare(java.lang.Object, java.lang.Object);
Code:
4: aload_1
5: checkcast // class Test3$Person
8: aload_2
9: checkcast // class Test$Person
12: invokevirtual Test$ComparisonProvider.compareByName:(Test$Person;Test$Person;)I
Java 8 allows us to use instances of classes as if they were implementing a particular interface, while actually they are not
Not exactly, it allows you to use a single method of some instance of a class as if it were implementing some functional interface.
And it doesn't add any functionality that didn't exist in Java 7 - it just gives you a short cut to writing that functionality.
For example, instead of:
Arrays.sort(someArray, someInstance::someMethod);
In Java 7 you could use anonymous class instance to write:
Arrays.sort(someArray, new Comparator<SomeType> () {
public int compare (SomeType one, SomeTypeTwo) {
return someInstance.someMethod(one,two);
}
});
As long as the instance method is accessible (i.e. public), you can use it as you see fit.
Comparator is a functional interface, which means that when requested you can pass an instance of a class implementing it, use a lambda expression that conforms to the type of single abstract method declared in it or use a method reference that also conforms to.
Java 8 Functional interface makes the difference. This tries to catch the concept of function. Afterall what is important in Comparator is not the type itself but the method (and its type) that should be provided at runtime. In pre Java 8 you need to provide a function object, while in Java 8 you can simply provide the function (just what is needed).
So for the type system everything is correct, provided that the lambdas or references you use are of the type of the method of the functional interface.

Method references to raw types harmful?

The code below contains a reference to Enum::name (notice no type parameter).
public static <T extends Enum<T>> ColumnType<T, String> enumColumn(Class<T> klazz) {
return simpleColumn((row, label) -> valueOf(klazz, row.getString(label)), Enum::name);
}
public static <T, R> ColumnType<T, R> simpleColumn(BiFunction<JsonObject, String, T> readFromJson,
Function<T, R> writeToDb) {
// ...
}
Javac reports a warning during compilation:
[WARNING] found raw type: java.lang.Enum missing type arguments for
generic class java.lang.Enum
Changing the expression to Enum<T>::name causes the warning to go away.
However Idea flags the Enum<T>::name version with a warning that:
Explicit type arguments can be inferred
In turn Eclipse (ECJ) doesn't report any problems with either formulation.
Which of the three approaches is correct?
On one hand raw types are rather nasty. If you try to put some other type argument e.g. Enum<Clause>::name will cause the compilation to fails so it's some extra protection.
On the other hand the above reference is equivalent to e -> e.name() lambda, and this formulation doesn't require type arguments.
Enviorment:
Java 8u91
IDEA 15.0.3 Community
ECJ 4.5.2
There is no such thing as a “raw method reference”. Whilst raw types exist to help the migration of pre-Generics code, there can’t be any pre-Generics usage of method references, hence there is no “compatibility mode” and type inference is the norm. The Java Language Specification §15.13. Method Reference Expressions states:
If a method or constructor is generic, the appropriate type arguments may either be inferred or provided explicitly. Similarly, the type arguments of a generic type mentioned by the method reference expression may be provided explicitly or inferred.
Method reference expressions are always poly expressions
So while you may call the type before the :: a “raw type” when it referes to a generic class without specifying type arguments, the compiler will still infer the generic type signature according to the target function type. That’s why producing a warning about “raw type usage” makes no sense here.
Note that, e.g.
BiFunction<List<String>,Integer,String> f1 = List::get;
Function<Enum<Thread.State>,String> f2 = Enum::name;
can be compiled with javac without any warning (the specification names similar examples where the type should get inferred), whereas
Function<Thread.State,String> f3 = Enum::name;
generates a warning. The specification says about this case:
In the second search, if P1, ..., Pn is not empty and P1 is a subtype of ReferenceType, then the method reference expression is treated as if it were a method invocation expression with argument expressions of types P2, ..., Pn. If ReferenceType is a raw type, and there exists a parameterization of this type, G<...>, that is a supertype of P1, the type to search is the result of capture conversion (§5.1.10) applied to G<...>;…
So in the above example, the compiler should infer Enum<Thread.State> as the parametrization of Enum that is a supertype of Thread.State to search for an appropriate method and come to the same result as for the f2 example. It somehow does work, though it generates the nonsensical raw type warning.
Since apparently, javac only generates this warning when it has to search for an appropriate supertype, there is a simple solution for your case. Just use the exact type to search:
public static <T extends Enum<T>> ColumnType<T, String> enumColumn(Class<T> klazz) {
return simpleColumn((row, label) -> valueOf(klazz, row.getString(label)), T::name);
}
This compiles without any warning.

"Jump to definition" for methods without external parameter names

For method calls with external parameter names I can cmd-click in Xcode on any parameter name to jump to the method definition. For
example, in
let a = Array(count: 3, repeatedValue: 0)
a cmd-click on "count" or "repeatedValue" jumps directly to the Array initializer method
init(count: Int, repeatedValue: Element)
However, I haven't found a way to do the same for methods calls
without external parameter names, as in
let c = Array("abc".characters)
Of course I can lookup that the characters method returns a String.CharacterView which in turn conforms to SequenceType, so this will call the Array initializer
init<S : SequenceType where S.Generator.Element == _Buffer.Element>(_ s: S)
but I wonder if somebody has found a direct "jump to definition" method
for this situation.
This would be very useful if a type has many overloaded init methods (without external parameter names), to determine which one is actually called.
The above examples are from Swift 2/Xcode 7 beta, but the problem is not
tied to a special Swift/Xcode version.
(Also posted at the Apple Developer Forums: https://forums.developer.apple.com/thread/12687.)
You have to do some work:
let c = Array.init("abc".characters)
// ^^^^^
Use initializer expression, then cmd + click on it.

Resources