Вопрос по дженерикам метода и ограничению переменных типов

110
30 ноября 2020, 10:50

Сейчас активно изучаю (или даже разбираю) известное пособие Хорстмена и Корнелла по Java2. В данный момент остановился на параметризации. Дошел до пункта "Обобщенные методы" и следующий за ним "Ограничения переменных типов". И в ближайшем листинге среди прочего кода появляется такая сигнатура:

public static <T extends Comparable> Pair<T> minmax(T[] a)

Я понимаю, что в методе применяется метод compareTo(), принадлежащий интерфейсу Comparable и поэтому мы должны "защитить" наш метод от того, чтобы туда не затесался класс, который не реализует данный интерфейс. Ок, понятно. Но почему бы тогда нельзя было написать:

public static Pair<T extends Comparable> minmax(T[] a)

С другой стороны, возможно имелось в виду, что это не объект класса Pair должен возвращать метод, а объект любого класса, реализующий Comparable? Тогда почему не написать так:

public static <T extends Comparable> T minmax(T[] a)

?

Я не отрицаю, что в пособии вполне может быть указана правильная сигнатура. И если это так, то прошу подробно описать ее. Заранее спасибо за развернутые ответы!

Answer 1

Вы хотите развёрнутых ответов? Их есть у меня! Немного теории по параметризованным методам.

Если метод нестатический, а класс - параметризованный, то параметр типа наследуется из описания класса:

public class A<T> {
    pubic void doSth(T t) { // метод принимает аргумент типа T
        System.out.println(t.getClass().getName());
    }
}
new A<String>().doSth("abc");   // выведет java.lang.String
new A<String>().doSth(123);     // не скомпилируется

Если метод статический, то унаследовать параметр типа от класса он не может. Это вызвано тем, что параметр типа привязывается к конкретному объекту при его создании, а статический метод не привязан к конкретному объекту, он привязан к классу в целом. В случае статического метода параметр типа нужно указывать непосредственно перед объявлением метода:

public class A {
    public static <T> void doSth(T t) {
        System.out.println(t.getClass().getName());
    }
}
A.doSth("abc");         // выведет java.lang.String
A.doSth(123);           // выведет java.lang.Integer

В этом случае тип T определяется в момент вызова статического метода по типу передаваемого аргумента.

Ещё один случай, когда может понадобиться явно параметризовать метод - это нестатический метод, параметр типа которого должен отличаться от параметра типа класса:

public class A<T>
{
    T t;
    public A(T t) {
        this.t = t;
    }
    public <Z> void doSth(Z z) {
        System.out.println(t.getClass().getName());
        System.out.println(z.getClass().getName());
    }
}
new A<String>("abc").doSth(123); // выведет java.lang.String
                                 //         java.lang.Integer

Если в момент компиляции вычислить тип параметра метода невозможно, необходимо явно указать его при вызове. Такие случаи очень редки, поэтому пример несколько синтетический:

public class A
{
    public static void printValue(String val) {
        System.out.println("String");
    }
    public static void printValue(Integer val) {
        System.out.println("Integer");
    }
    public static <T> T getValue() {
        return null;
    }
}
A.printValue(A.getValue());          // не скомпилируется - неясно, какую
                                     // перегрузку printValue вызывать
A.printValue(A.<String>getValue());  // выведет String
A.printValue(A.<Integer>getValue()); // выведет Integer

Теперь непосредственно к вашему вопросу. Нельзя просто написать

public static Pair<T extends Comparable> minmax(T[] a)

потому что Java не знает, что за тип T. Java не может унаследовать его от объявления класса (даже если класс параметризован), так как метод minimax статический. Поэтому нужно явно прописывать параметр типа в сигнатуре метода:

public static <T extends Comparable> Pair<T> minmax(T[] a)

Почему нельзя написать

public static <T extends Comparable> T minmax(T[] a)

? Можно, это корректная сигнатура метода. Однако, судя по названию метода, он должен возвращать одновременно минимальное и максимальное значение переданного массива. Поэтому метод возвращает объект класса Pair<T>, который содержит в себе два значения.

Answer 2

Сигнатура абсолютна верна.

Первый предложенный вами вариант исключается, потому что в статическом методе нельзя использовать типы, определенные в классе. Вам этого не позволит компилятор.

Второй вариант нелогичен, т.к. метод возвращает пару значений (min, max), а для этого нужен какой-то контейнер, например Pair. В данном случае предполагается, что оба элемента в кортеже имеют одинаковый тип.

READ ALSO
Java / C# Почему одна функция с одинаковыми параметрами показывает разное время выполнения?

Java / C# Почему одна функция с одинаковыми параметрами показывает разное время выполнения?

Во время написания программы у меня возник вопрос, ответ на который я не смог найтиЯ замеряю время выполнения одной функции в наносекундах...

97
string &ldquo;2&rdquo; не конвертируется в int 2

string “2” не конвертируется в int 2

передаю по датаграмм каналу количество элементов коллекции

91