问题
I recently put my hands on Java 8 and tried using Method References. I was trying different kinds of Method references and got stuck in the type "Reference to an Instance Method of an Arbitrary Object of a Particular Type".
String[] arr = {"First", "Second", "Third", "Fourth"};
Arrays.sort(arr, String::compareToIgnoreCase);
This works perfectly well. But when I try to refer a method of a user defined class through its type :
Demo2[] arr = {a, b};
Arrays.sort(arr, Demo2::compare);
This displays compile-time error as "Non-static method cannot be referenced from a static context".
Here's the Demo2 class :
public class Demo2 implements Comparator<Demo2> {
Integer i;
Demo2(Integer i1){
i = i1;
}
public Integer getI() {
return i;
}
@Override
public int compare(Demo2 o1, Demo2 o2) {
return o1.getI().compareTo(o2.getI());
}
}
回答1:
As greg-449 pointed to you, your code has a bug.
By making a method reference like YourObjet::yourMethod
you make a static reference to the method of the instance. So the method will be called for each object and thus the signature needs to be different than the earlier
A code that will compile will be of the following form :
Demo2[] demos = {a, b};
Arrays.sort(demos, Demo2::compareTo);
public class Demo2 {
Integer i;
Demo2(Integer i1){
i = i1;
}
public Integer getI() {
return i;
}
public int compareTo(Demo2 other) {
return this.getI().compareTo(other.getI());
}
}
But as RealSkeptic pointed out, this is not the correct way to implement and objet comparison. You should give the Arrays.sort method a comparator instead :
Arrays.sort(demos, (obj1, obj2) -> obj1.getI().compareTo(obj2.getI()));
回答2:
The Comparator
interface required for Arrays.sort(T[],Comparator<T>)
has a method that accepts two object references of the same type T
, and returns an integer.
There is a bit of "magic" in method references. What Java does is wrap the method in such a way that it will fit the interface requirement.
The interface, of course, doesn't require a static method. But the wrapping can create a method that calls a static method, as in the Tutorial's first example:
public static int compareByAge(Person a, Person b) {
return a.birthday.compareTo(b.birthday);
}
It wraps it in such a way that you get something similar to
new Comparator<Person>() {
@Override
public int compare(Person a, Person b) {
return Person.compareByAge(a,b);
}
}
Which satisfies the interface.
But in the example in the section "Reference to an Instance Method of an Arbitrary Object of a Particular Type", it wraps it differently. It needs a method that receives two strings, but it has a method that only receives one. This is how String::compareToIgnoreCase
is defined:
public int compareToIgnoreCase(String str)
But in this case, it's an instance method. What Java does now is, because this method belongs to an object of type String
, and accepts an object of type String
it is easy to build a "wrap" around it that makes it into a method that accepts two objects, much like the lambda expression:
(String a, String b) -> {
return a.compareToIgnoreCase( b );
}
Or, if we imagine a formal wrapping as a Comparator
:
new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareToIgnoreCase(b);
}
}
So, the fact that it's an instance method that belongs to type T
, accepts type T
and returns int
allows Java to wrap it appropriately so it fits the Comparator
interface.
But int compare(Demo2 o1, Demo2 o2)
doesn't fit that pattern. It accepts two parameters. If a method accepts two parameters, it must be a static method to fit the wrapping rules - there is no way to pass the "this" object into the Comparator
interface. So it tries to wrap it as a static method, and fails, as it is not a static method.
Bottom line: you got the error, because for this particular type of method reference, you need an instance method with only one parameter of the same type as the class.
As @Holger mentions in a comment, if you have a new class you are building, you shouldn't put a comparison method in it specifically for this sort of sorting task. If the class has a natural ordering, make it Comparable
and use Arrays.sort(Object[])
. If it doesn't, and you need to sort it sometimes based on any of its attributes, use a lambda expression or Comparator.comparing(Demo2::getI)
which makes better use of an existing getter for the specific purpose of comparison.
回答3:
As a convention, Comparator<T>
is implemented with a lambda expression and it looks very odd to implement it in a class.
Demo2[] array = new Demo2[2];
array[0] = new Demo2(12);
array[1] = new Demo2(32);
Comparator<Demo2> demo2Comparator = (e1,e2)->e1.getI().compareTo(e2.getI());
Arrays.sort(array, demo2Comparator);
来源:https://stackoverflow.com/questions/39073799/type-inference-in-method-reference