Перейдите к своему config.php
. У меня такая же проблема. Проверьте имя пользователя и пароль, а также sql select - это то же имя, что и config.
Есть определенные места, где групповые символы и параметры типа делают то же самое. Но есть также определенные места, где вам нужно использовать параметры типа.
Взяв ваш метод в качестве примера, предположим, что вы хотите убедиться, что список 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
Ссылки:
Я постараюсь ответить на ваш вопрос один за другим.
Разве мы не считаем, что wild card like
blockquote >(Collection<? extends E> c);
также поддерживает тип полиморфизма?№. Причина в том, что ограниченный подстановочный символ не имеет определенного типа параметра. Это неизвестно. Все это «знает», что «сдерживание» относится к типу
E
(независимо от того, что определено). Таким образом, он не может проверить и обосновать, соответствует ли предоставленное значение ограниченному типу.Таким образом, не имеет смысла иметь полиморфное поведение в подстановочных знаках.
Документ обескураживает второй объявления и способствует использованию первого синтаксиса? В чем разница между первой и второй декларацией?
blockquote>Первый вариант лучше в этом случае, так как
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
является параметризованным типом, который требует привязки.Надеюсь, это поможет.
Рассмотрим следующий пример из 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». Первый метод в книге отсутствует.
В вашем первом вопросе: это означает, что если существует связь между типом параметра и типом возвращаемого метода, то используйте общий.
Например:
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. */
Метод подстановочных знаков также является общим - вы можете называть его некоторым диапазоном типов.
Синтаксис <T>
определяет имя переменной типа. Если переменная типа имеет какое-либо применение (например, в реализации метода или в качестве ограничения для другого типа), тогда имеет смысл назвать ее, иначе вы могли бы использовать ?
в качестве анонимной переменной. Таким образом, выглядит просто короткое сокращение.
Кроме того, синтаксис ?
нельзя избежать, когда вы объявляете поле:
class NumberContainer
{
Set<? extends Number> numbers;
}
Насколько я понимаю, существует только один вариант использования, когда групповой символ строго необходим (т. е. может выражать то, что вы не можете выразить с использованием явных параметров типа). Это означает, что вам нужно указать нижнюю границу.
Кроме того, однако, маскировки служат для написания более сжатого кода, как описано в следующем документе в документе, который вы упомянули:
Общие методы позволяют использовать параметры типа для выражения зависимостей между типами одного или нескольких аргументов метода и / или его возвращаемого типа. Если нет такой зависимости, общий метод не должен использоваться.
[...]
Использование подстановочных знаков более четкое и краткое, чем объявление явных параметров типа, и должен быть предпочтительным, когда это возможно.
[...]
Подстановочные знаки также имеют то преимущество, что их можно использовать вне сигнатур методов, поскольку типы полей, локальные переменные и массивы.
blockquote>
Еще одно отличие, которое здесь не указано.
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
}
}