Разница между параметром типа & lt; T & gt; и неограниченный подстановочный знак & lt; & gt ;? [Дубликат]

Перейдите к своему config.php. У меня такая же проблема. Проверьте имя пользователя и пароль, а также sql select - это то же имя, что и config.

93
задан emlai 19 December 2015 в 19:53
поделиться

7 ответов

Есть определенные места, где групповые символы и параметры типа делают то же самое. Но есть также определенные места, где вам нужно использовать параметры типа.

  1. Если вы хотите применить некоторые отношения к различным типам аргументов метода, вы не можете сделать это с помощью подстановочных знаков, вы должны использовать параметры типа.

Взяв ваш метод в качестве примера, предположим, что вы хотите убедиться, что список src и dest, переданный методу copy(), должен быть одного и того же параметризованного типа, вы можете сделать это с помощью введите такие параметры:

public static <T extends Number> void copy(List<T> dest, List<T> src)

Здесь вы убедитесь, что оба dest и src имеют одинаковый параметризованный тип для List. Таким образом, можно скопировать элементы из src в dest.

Но если вы измените метод на использование подстановочного знака:

public static void copy(List<? extends Number> dest, List<? extends Number> src)

t работает должным образом. Во втором случае вы можете передать List<Integer> и List<Float> как dest и src. Таким образом, перемещение элементов из src в dest больше не будет безопасным типом. Если вам не нужны такие отношения, вы можете вообще не использовать параметры типа.

. Другая разница между использованием подстановочных знаков и параметрами типа:

  • Если у вас есть только один аргумент с параметризованным типом, вы можете использовать подстановочный знак, хотя параметр типа также будет работать.
  • Параметры типа поддерживают множественные границы, подстановочные знаки - нет.
  • Поддержка подстановочных знаков как верхняя, так и нижняя границы, параметры типа просто поддерживают верхние границы. Итак, если вы хотите определить метод, который принимает List типа Integer или суперкласс, вы можете сделать:
    public void print(List<? super Integer> list)  // OK
    
    , но вы не можете использовать параметр типа:
     public <T super Integer> void print(List<T> list)  // Won't compile
    

Ссылки:

124
ответ дан Rohit Jain 25 August 2018 в 23:02
поделиться

Я постараюсь ответить на ваш вопрос один за другим.

Разве мы не считаем, что wild card like (Collection<? extends E> c); также поддерживает тип полиморфизма?

№. Причина в том, что ограниченный подстановочный символ не имеет определенного типа параметра. Это неизвестно. Все это «знает», что «сдерживание» относится к типу E (независимо от того, что определено). Таким образом, он не может проверить и обосновать, соответствует ли предоставленное значение ограниченному типу.

Таким образом, не имеет смысла иметь полиморфное поведение в подстановочных знаках.

Документ обескураживает второй объявления и способствует использованию первого синтаксиса? В чем разница между первой и второй декларацией?

Первый вариант лучше в этом случае, так как T всегда ограничен, а source обязательно будет иметь значения (неизвестных), что подклассы T.

Итак, предположим, что вы хотите скопировать весь список чисел, первая опция будет

Collections.copy(List<Number> dest, List<? extends Number> src);

src, по существу, может принять List<Double> , List<Float> и т. д., так как существует верхняя граница параметризованного типа, найденная в dest.

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

//For double 
Collections.copy(List<Number> dest, List<Double> src); //Double extends Number.

//For int
Collections.copy(List<Number> dest, List<Integer> src); //Integer extends Number.

Поскольку S является параметризованным типом, который требует привязки.

Надеюсь, это поможет.

2
ответ дан Buhake Sindi 25 August 2018 в 23:02
поделиться

Рассмотрим следующий пример из Java Programming by James Gosling, в котором мы хотим объединить 2 SinglyLinkQueue:

public static <T1, T2 extends T1> void merge(SinglyLinkQueue<T1> d, SinglyLinkQueue<T2> s){
    // merge s element into d
}

public static <T> void merge(SinglyLinkQueue<T> d, SinglyLinkQueue<? extends T> s){
        // merge s element into d
}

Оба вышеупомянутых метода имеют одинаковую функциональность. Итак, что предпочтительнее? Ответ второй. В собственных словах автора:

«Общее правило заключается в использовании подстановочных знаков, когда вы можете, потому что код с подстановочными знаками, как правило, более читабельен, чем код с несколькими параметрами типа. При принятии решения о необходимости переменной типа спросите себя если эта переменная типа используется для связи двух или более параметров или для связывания типа параметра с типом возвращаемого значения. Если ответ отрицательный, тогда необходимо создать подстановочный знак. "

Примечание. В книге указывается только второй метод и имя параметра типа S вместо «T». Первый метод в книге отсутствует.

3
ответ дан chammu 25 August 2018 в 23:02
поделиться

В вашем первом вопросе: это означает, что если существует связь между типом параметра и типом возвращаемого метода, то используйте общий.

Например:

public <T> T giveMeMaximum(Collection<T> items);
public <T> Collection<T> applyFilter(Collection<T> items);

Здесь вы извлекаете часть Т, следуя определенным критериям. Если T Long, ваши методы вернут Long и Collection<Long>; фактический тип возврата зависит от типа параметра, поэтому полезно и рекомендуется использовать общие типы.

Если это не так, вы можете использовать типы диких карт:

public int count(Collection<?> items);
public boolean containsDuplicate(Collection<?> items);

В этом двух примерах, независимо от типа элементов в коллекциях, типами возврата будут int и boolean.

В ваших примерах:

interface Collection<E> {
    public boolean containsAll(Collection<?> c);
    public boolean addAll(Collection<? extends E> c);
}

эти две функции возвращают логическое значение всех типов элементов в коллекциях. Во втором случае он ограничен экземплярами подкласса E.

Второй вопрос:

class Collections {
    public static <T> void copy(List<T> dest, List<? extends T> src) {
    ...
}

Этот первый код позволяет вам передавать гетерогенный List<? extends T> src в качестве параметра , Этот список может содержать несколько элементов разных классов, если все они расширяют базовый класс T.

, если у вас есть:

interface Fruit{}

и

class Apple implements Fruit{}
class Pear implements Fruit{}
class Tomato implements Fruit{}

вы могли бы сделать

List<? extends Fruit> basket = new ArrayList<? extends Fruit>();
basket.add(new Apple());
basket.add(new Pear());
basket.add(new Tomato());
List<Fruit> fridge = new ArrayList<Fruit>(); 

Collections.copy(fridge, basket);// works 

С другой стороны,

class Collections {
    public static <T, S extends T> void copy(List<T> dest, List<S> src) {
    ...
}

ограничивают List<S> src одним классом S, который является подклассом T. Список может содержат только элементы одного класса (в данном случае S) и другого класса, даже если они также реализуют T. Вы не сможете использовать мой предыдущий пример, но вы могли бы сделать:

List<Apple> basket = new ArrayList<Apple>();
basket.add(new Apple());
basket.add(new Apple());
basket.add(new Apple());
List<Fruit> fridge = new ArrayList<Fruit>();

Collections.copy(fridge, basket); /* works since the basket is defined as a List of apples and not a list of some fruits. */
8
ответ дан Diyoda_ 25 August 2018 в 23:02
поделиться

Метод подстановочных знаков также является общим - вы можете называть его некоторым диапазоном типов.

Синтаксис <T> определяет имя переменной типа. Если переменная типа имеет какое-либо применение (например, в реализации метода или в качестве ограничения для другого типа), тогда имеет смысл назвать ее, иначе вы могли бы использовать ? в качестве анонимной переменной. Таким образом, выглядит просто короткое сокращение.

Кроме того, синтаксис ? нельзя избежать, когда вы объявляете поле:

class NumberContainer
{
 Set<? extends Number> numbers;
}
2
ответ дан kan 25 August 2018 в 23:02
поделиться

Насколько я понимаю, существует только один вариант использования, когда групповой символ строго необходим (т. е. может выражать то, что вы не можете выразить с использованием явных параметров типа). Это означает, что вам нужно указать нижнюю границу.

Кроме того, однако, маскировки служат для написания более сжатого кода, как описано в следующем документе в документе, который вы упомянули:

Общие методы позволяют использовать параметры типа для выражения зависимостей между типами одного или нескольких аргументов метода и / или его возвращаемого типа. Если нет такой зависимости, общий метод не должен использоваться.

[...]

Использование подстановочных знаков более четкое и краткое, чем объявление явных параметров типа, и должен быть предпочтительным, когда это возможно.

[...]

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

0
ответ дан Martin Maletinsky 25 August 2018 в 23:02
поделиться

Еще одно отличие, которое здесь не указано.

static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
    for (T o : a) {
        c.add(o); // correct
    }
}

Но следующее приведет к ошибке времени компиляции.

static <T> void fromArrayToCollection(T[] a, Collection<?> c) {
    for (T o : a) {
        c.add(o); // compile time error
    }
}
2
ответ дан Rob 25 August 2018 в 23:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: