When to use generic methods and when to use wild-card?
Когда использовать универсальные методы, а когда использовать подстановочную карту?
Я читаю об универсальных методах из OracleDocGenericMethod. Я довольно смущен сравнением, когда говорится, когда использовать шаблонную карту, а когда использовать универсальные методы. Цитирую из документа.
Вместо этого мы могли бы использовать здесь универсальные методы:
interfaceCollection<E> { public <T> booleancontainsAll(Collection<T> c); public <T extendsE> booleanaddAll(Collection<T> c); // Hey, type variables can have bounds too! }
[…] Это говорит нам о том, что аргумент типа используется для полиморфизма; его единственный эффект заключается в том, что допускается использование множества реальных типов аргументов на разных сайтах вызова. Если это так, то следует использовать подстановочные знаки. Подстановочные знаки предназначены для поддержки гибкого подтипирования, что мы и пытаемся здесь выразить.
Не кажется ли нам, что подстановочная карта, подобная (Collection<? extends E> c);, также поддерживает разновидность полиморфизма? Тогда почему использование универсального метода в этом случае считается нехорошим?
Продолжая, в нем говорится,
Универсальные методы позволяют использовать параметры типа для выражения зависимостей между типами одного или нескольких аргументов метода и / или его возвращаемого типа. Если такой зависимости нет, универсальный метод не следует использовать.
Документ не поощряет второе объявление и поощряет использование первого синтаксиса? В чем разница между первым и вторым объявлением? Кажется, что оба делают одно и то же?
Может кто-нибудь пролить свет на эту область.
Переведено автоматически
Ответ 1
Есть определенные места, где подстановочные знаки и параметры типа выполняют одно и то же действие. Но есть также определенные места, где вы должны использовать параметры типа.
Если вы хотите установить некоторую взаимосвязь для различных типов аргументов метода, вы не можете сделать это с помощью подстановочных знаков, вы должны использовать параметры типа.
Взяв в качестве примера ваш метод, предположим, вы хотите убедиться, что src и dest список, передаваемый copy() методу, должен быть одного и того же параметризованного типа, вы можете сделать это с параметрами типа, например, так:
это будет работать не так, как ожидалось. Во втором случае вы можете передать List<Integer> and List<Float> как dest и src. Итак, перемещение элементов из src в dest больше не будет типобезопасным. Если вам не нужна такая связь, то вы можете вообще не использовать параметры типа.
Некоторые другие различия между использованием подстановочных знаков и параметров типа заключаются в следующем:
Если у вас есть только один параметризованный аргумент типа, вы можете использовать подстановочный знак, хотя параметр типа также будет работать.
Параметры типа поддерживают несколько границ, подстановочные знаки - нет.
Подстановочные знаки поддерживают как верхнюю, так и нижнюю границы, параметры типа поддерживают только верхние границы. Итак, если вы хотите определить метод, который принимает List тип Integer или это суперкласс, вы можете сделать:
publicvoidprint(List<? super Integer> list)// OK
но вы не можете использовать параметр типа:
public <T super Integer> voidprint(List<T> list)// Won't compile
Рассмотрим следующий пример из "Программирования на Java" Джеймса Гослинга, 4-е издание ниже, где мы хотим объединить 2 SinglyLinkQueue:
publicstatic <T1, T2 extendsT1> voidmerge(SinglyLinkQueue<T1> d, SinglyLinkQueue<T2> s){ // merge s element into d }
publicstatic <T> voidmerge(SinglyLinkQueue<T> d, SinglyLinkQueue<? extends T> s){ // merge s element into d }
Оба вышеперечисленных метода обладают одинаковой функциональностью. Итак, какой из них предпочтительнее? Ответ 2-й. По собственным словам автора :
"Общее правило заключается в использовании подстановочных знаков, когда вы можете, потому что код с подстановочными знаками обычно более удобочитаем, чем код с параметрами нескольких типов. Решая, нужна ли вам переменная типа, спросите себя, используется ли эта переменная типа для связи двух или более параметров или для связи типа параметра с возвращаемым типом. Если ответ отрицательный, то подстановочного знака должно быть достаточно. "
Примечание: В книге указан только второй метод, а имя параметра типа - S вместо 'T'. Первого метода в книге нет.
Ответ 3
В вашем первом вопросе: это означает, что если существует связь между типом параметра и типом возвращаемого метода, тогда используйте универсальный.
Например:
public <T> T giveMeMaximum(Collection<T> items); public <T> Collection<T> applyFilter(Collection<T> items);
Здесь вы извлекаете некоторые из T, следуя определенным критериям. Если T равно Long, ваши методы будут возвращать Long и Collection<Long>; фактический тип возвращаемого значения зависит от типа параметра, поэтому полезно и рекомендуется использовать универсальные типы.
Когда это не так, вы можете использовать подстановочные типы:
эти две функции будут возвращать логическое значение независимо от типов элементов в коллекциях. Во втором случае это ограничено экземплярами подкласса E.
Этот первый код позволяет передавать гетерогенный List<? extends T> src в качестве параметра. Этот список может содержать несколько элементов разных классов, если все они расширяют базовый класс T.
ограничивать List<S> src принадлежностью к одному определенному классу S, который является подклассом T. Список может содержать элементы только одного класса (в данном экземпляре S) и никакого другого класса, даже если они также реализуют T . Вы не сможете использовать мой предыдущий пример, но вы могли бы сделать:
Collections.copy(fridge, basket); /* works since the basket is defined as a List of apples and not a list of some fruits. */
Ответ 4
Метод подстановочного знака также является универсальным - вы могли бы вызывать его с некоторым диапазоном типов.
В <T> синтаксисе определяется имя переменной типа. Если переменная типа имеет какое-либо применение (например, в реализации метода или в качестве ограничения для другого типа), то имеет смысл дать ей имя, в противном случае вы могли бы использовать ?, как анонимную переменную. Итак, похоже, это просто короткий путь.
Более того, ? синтаксиса невозможно избежать при объявлении поля: