0

I have a simple java class with some variables String, Double, Integer etc.

public class MyClass {
    private String name;
    private String status;
    private Double risk;
    private Double health;
    // more variables with all getters and setters
}

I am trying to write a generic method which will return the Function<T, U> type. I will further use this function for sorting. It is throwing compile time error

The type of getName() from the type MyClass is String, this is incompatible with the descriptor's return type: U

at line func = MyClass::getName;

and error

The type of getRisk() from the type MyClass is Double, this is incompatible with the descriptor's return type: U

at line func = MyClass::getRisk;

     private static <T extends MyClass, U extends Comparable<U>> Function<T, U> getSortFunction(final String fieldName) {
        Function<T, U> func = null;
        if ("name".equalsIgnoreCase(fieldName)) {
            func = MyClass::getName;
        } else if ("risk".equalsIgnoreCase(fieldName)) {
            func = MyClass::getRisk;
        }
        return func;
    }

The return type of my function is of Comparable type not sure what wrong I am doing here. Any pointers?

sumeet610
  • 3
  • 2
  • It's because the compiler has no guarantee that `U` will be a `String` or `Double` or whatever else you know it to be. You may have to use casting for this, although your real problem may be something else. – user Sep 19 '20 at 20:22

2 Answers2

0

<T extends MyClass, U extends Comparable<U>> : This is type inference problem. This has be discussed in a question: Very confused by Java 8 Comparator type inference

To fix the problem you can do the following:

  • Change the return type: private static <T extends MyClass, U extends Comparable<U>> Function<T, U> to private static Comparator<MyClass>?

  • Return Comparator.comparing based on the field name:

if ("name".equalsIgnoreCase(fieldName)) {
            func = Comparator.comparing(MyClass::getName);

Complete code:

    private static Comparator<MyClass> getSortFunction(final String fieldName) {
        Comparator<MyClass> func = null;
        if ("name".equalsIgnoreCase(fieldName)) {
            func = Comparator.comparing(MyClass::getName);
        } else if ("risk".equalsIgnoreCase(fieldName)) {
            func = Comparator.comparing(MyClass::getRisk);
        }
        return func;
    }

    public static void main(String[] args) {
        List<MyClass> myClassList = //init list;
        myClassList.sort(getSortFunction("name"));
    }


gtiwari333
  • 24,554
  • 15
  • 75
  • 102
0

Java Generics is all about you as developer promising that the instances used/returned will fulfill some type restrictions.

So, what is it that you promise about the result type of getSortFunction()? You write:

private static <T extends MyClass, U extends Comparable<U>> Function<T, U> getSortFunction(final String fieldName)

meaning:

  • The method will return a Function accepting T and returning U.
  • The Function will accept some unspecified subclass T of MyClass.
  • The Function will return instances of U, being some Comparable class.

When the compiler sees a call of e.g. getSortFunction("name"), he has no hint what to conclude for T and U, whether the resulting Function will be a Function<MyClass,String> or a Function<MySubclass,Long>, so the generics part of your declaration isn't helpful. The compiler's only chance is to look at the expected result of the method call, e.g.

Function<MyClass,String> fun = getSortFunction("hometown");

In this case, T is MyClass, and U is String. Of course, the method body must be written in a way applicable to all possible call patterns, so the same method body must match both a call expecting a Function<MyClass,String> and one expecting a Function<MySubclass,Long>. Returning MyClass::getName only matches the first call pattern and not e.g. a Function<MySubclass,Long>.

That's why you get the compile error.

Ralf Kleberhoff
  • 6,990
  • 1
  • 13
  • 7
  • Since I have declared in the return type(>) the compiler should use be using that to conclude. T should evaluate to MyClass and U should evaluate to Comparable. The compiler should be using the Comparable instead of evaluating any condition. There is no point in using extends then. – sumeet610 Sep 21 '20 at 05:41
  • If you don't want variability in the result type, why do you introduce the type variables T and U at all? – Ralf Kleberhoff Sep 21 '20 at 06:49