Я пришел по следующему коду:
public static <T> Set<T> distinct(
Collection<? extends T> list,
Comparator<? super T> comparator) {
Set<T> set = new TreeSet<>(comparator);
set.addAll(list);
return set;
}
Этот код просто использует промежуточный TreeSet
для удаления дубликатов, где равенство между элементами определяется в соответствии с предоставленным компаратором.
Пусть дает локальный тип вывода возможность, я (наивно) думал... Поэтому я изменил приведенный выше код на:
public static <T> Set<T> distinct(
Collection<? extends T> list,
Comparator<? super T> comparator) {
var set = new TreeSet<>(comparator);
set.addAll(list);
return set;
}
Это имело смысл для меня, потому что тип set
можно вывести из типа comparator
, или так я думал. Однако модифицированный код не компилируется и генерирует следующую ошибку:
java: incompatible types: java.util.TreeSet<capture#1 of ? super T> cannot be converted to java.util.Set<T>
Теперь я понимаю, почему возникает ошибка, и я признаю, что тип компаратора на самом деле является Comparator<? super T>
Comparator<? super T>
, поэтому тип, выводимый var
- TreeSet<? super T>
TreeSet<? super T>
.
Однако мне интересно, почему var
не может вывести общий тип TreeSet
только как T
вместо ? super T
? super T
В конце концов, (java.util.Comparator) rel="nofollow noreferrer">согласно документам, TreeSet<E>
имеет конструктор, который принимает аргумент типа Comparator<? super E>
Comparator<? super E>
. Поэтому для вызова этого конструктора следует создать TreeSet<E>
, а не TreeSet<? super E>
TreeSet<? super E>
. (Это показывает первый фрагмент). Я ожидал, что var
будет следовать этой же логике.
Примечание 1: Один из способов сделать компиляцию кода - изменить тип возвращаемого значения на Set<? super T>
Set<? super T>
. Однако это вряд ли можно использовать...
Примечание 2: Другой способ заключается в том, чтобы не использовать контравариантность в компараторе, но я не хочу этого, потому что я не смог бы использовать Comparator
который сравнивает предков T
Примечание 3: Я знаю, что первый фрагмент работает, поэтому кажется очевидным, что я не должен использовать var
и объявлять набор явно как Set<T>
. Однако мой вопрос заключается не в том, должен ли я отказаться от второго фрагмента или как его исправить. Вместо этого я хотел бы знать, почему var
не TreeSet<T>
как тип set
локальной переменной в моем втором фрагменте.
EDIT 1: В этом комментарии пользователь @nullpointer правильно указывает, что я должен сделать следующее тонкое изменение, чтобы сделать компиляцию второго фрагмента:
var set = new TreeSet<T>(comparator); // T brings in the magic!
Теперь общий параметр типа T
является явным для TreeSet
, поэтому var
правильно вводит тип set
локальной переменной как TreeSet<T>
. Тем не менее, я хотел бы знать, почему я должен указать T
явно.
EDIT 2: В этом другом комментарии пользователь @Holger ловко упоминает, что на языке запрещено:
var set = new TreeSet<? super T>(comparator);
Приведенный выше код не может скомпилироваться со следующей ошибкой:
java: unexpected type
required: class or interface without bounds
found: ? super T
Итак, теперь вопрос становится более очевидным: если я не могу явно указать ограниченный общий тип ? super T
? super T
в выражении создания экземпляра new TreeSet<? super T>(comparator)
new TreeSet<? super T>(comparator)
, почему компилятор выводит TreeSet<? super T>
TreeSet<? super T>
как тип set
локальной переменной?