Type inference in method

I recently laid hands on Java 8 and tried using method references. I tried different types of method references and got stuck in the type "Link to an instance method of an arbitrary object of a certain type".

String[] arr = {"First", "Second", "Third", "Fourth"}; Arrays.sort(arr, String::compareToIgnoreCase); 

This works great. But when I try to pass a method of a user-defined class through its type:

 Demo2[] arr = {a, b}; Arrays.sort(arr, Demo2::compare); 

Shows a compile-time error because "The non-static method cannot refer to 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()); } } 
+3
java java-8 method-reference
source share
3 answers

As greg-449 pointed out to you, your code has an error.

By making a reference to a method, for example, YourObjet::yourMethod , you will create a static reference to the instance method. Thus, the method will be called for each object, and therefore, the signature must be different from the previous one

The code that will be compiled will look like this:

 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 noted, this is not the right way to implement and compare objects. Instead, you should specify the Arrays.sort method instead of the comparator:

  Arrays.sort(demos, (obj1, obj2) -> obj1.getI().compareTo(obj2.getI())); 
+5
source share

The Comparator interface needed for Arrays.sort(T[],Comparator<T>) has a method that takes two references to objects of the same type T and returns an integer.

There is a bit of magic in the method links. What Java does is to wrap the method in such a way that it meets the interface requirement.

The interface, of course, does not require a static method. But the shell can create a method that calls the static method, as in the first Tutorial example:

 public static int compareByAge(Person a, Person b) { return a.birthday.compareTo(b.birthday); } 

He wraps it in such a way that you get something like

 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 "Link to an instance method of an arbitrary object of a certain type", it wraps it in different ways. He needs a method that gets two strings, but has a method that gets only one. Here's how String::compareToIgnoreCase defined:

 public int compareToIgnoreCase(String str) 

But in this case, it is an instance method. What Java is doing now, because this method refers to an object of type String and accepts an object of type String , it is easy to build a wrapper around it, which turns it into a method that accepts two objects, very similar to the lambda expression:

 (String a, String b) -> { return a.compareToIgnoreCase( b ); } 

Or, if you imagine formal packaging as a Comparator :

 new Comparator<String>() { @Override public int compare(String a, String b) { return a.compareToIgnoreCase(b); } } 

So, the fact that this is an instance method that belongs to type T , takes type T and returns int , allows Java to wrap it appropriately so that it matches the Comparator interface.

But int compare(Demo2 o1, Demo2 o2) does not match this pattern. It takes two parameters. If a method accepts two parameters, it must be a static method to comply with the wrapping rules - there is no way to pass the "this" object to the Comparator interface. Therefore, it tries to wrap it as a static method and fails because it is not a static method.

Bottom line: you got an 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 notes in a comment, if you have a new class that you are building, you should not put the comparison method in it specifically for this sort of sorting task. If the class has a natural order, make it Comparable and use Arrays.sort(Object[]) . If this is not the case, and you need to sort it sometimes based on any of its attributes, use the lambda or Comparator.comparing(Demo2::getI) , which makes better use of the existing getter for the specific purpose of comparison.

+3
source share

As a convention, Comparator<T> is implemented using a lambda expression, and it looks very strange 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); 
+1
source share

All Articles