Tom
Tom

Reputation: 6342

Confused with class level generics parameter and method level generics parameter

The following is the interface signature of Comparator interface, it is a generic interface using T as the generics parameter.

public interface Comparator<T> {
 ....
}

The interface has a default method defined as follows:

    public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)

This method is a generic method with 2 generic parameters, T and U, It looks to me that T is also defined at method level as U, and has no relation with the T in Comparator<T>?

But with the following code,it looks that both T are same? I am confused.

import java.io.File;
import java.util.Comparator;

class Inner {
    public static String getValue(File f) {
        return f.getName();
    }
    public static String getValue2(Integer i) {
        return "" + i;
    }
}

public class Question {
    public static void main(String[] args) {
//getValue must use File as the parameter.
        Comparator<File> fileComparator = Comparator.comparing(Inner::getValue); 
    }
}

Upvotes: 0

Views: 38

Answers (1)

ruakh
ruakh

Reputation: 183466

This method is a generic method with 2 generic parameters, T and U, It looks to me that T is also defined at method level as U, and has no relation with the T in Comparator<T>?

Correct. In Java, a static method can't refer to the class's or interface's type parameters, because it's the same static method no matter what the type arguments are. (There isn't a Comparator<Integer>.comparing(...) and a Comparator<String>.comparing(...), just a single Comparator.comparing(...).)

So while it's totally fine for a static method to be generic, its type parameters are separate from those of the class or interface. (In this case that could be Comparator.<Integer>comparing(...) or Comparator.<String>comparing(...) or the like — note that the type argument is written after the dot — but in most cases you can just write Comparator.comparing(...) and let the compiler infer the right type arguments anyway.)

But with the following code,it looks that both T are same? I am confused.

They happen to be the same, yes. Comparator.<File>comparing(...) returns an instance of Comparator<File>, so basically any use of Comparator.<File>comparing(...) will have a corresponding use of Comparator<File>. (And likewise for any other type argument, obviously.)

In this case the compiler can tell that it's Comparator.<File>comparing(...) and not Comparator.<Integer>comparing(...), because the latter would return the wrong type; and it can therefore tell which Inner::getValue is meant, because only one of them has the right type to be passed into Comparator.<File>comparing(...).

Does that make sense?

Upvotes: 2

Related Questions