Почему использование Collections.emptySet() с дженериками работает в задании, но не как параметр метода?
Итак, у меня есть класс со следующим конструктором:
public FilterList(Set<Integer> labels) {
...
}
и я хочу построить новый объект FilterList
с пустым множеством. Следуя совету Джошуа Блоха в своей книге "Эффективная Java", я не хочу создавать новый объект для пустого набора; Я просто использую Collections.emptySet()
вместо:
FilterList emptyList = new FilterList(Collections.emptySet());
Это дает мне ошибку, жалуясь, что java.util.Set<java.lang.Object>
не является java.util.Set<java.lang.Integer>
. ОК, как насчет этого:
FilterList emptyList = new FilterList((Set<Integer>)Collections.emptySet());
Это также дает мне ошибку! Хорошо, как насчет этого:
Set<Integer> empty = Collections.emptySet();
FilterList emptyList = new FilterList(empty);
Эй, это работает! Но почему? В конце концов, у Java нет вывода типа, поэтому вы получаете предупреждение о непроверенных преобразованиях, если вы делаете Set<Integer> foo = new TreeSet()
вместо Set<Integer> foo = new TreeSet<Integer>()
. Но Set<Integer> empty = Collections.emptySet();
работает даже без предупреждения. Почему это?
Ответы
Ответ 1
Короткий ответ - это ограничение вывода типа в общей системе Java. Он может вызывать общие типы против конкретных переменных, но не против параметров метода.
Я подозреваю, что это связано с тем, что методы динамически отправляются в зависимости от класса среды выполнения объекта-владельца, поэтому во время компиляции (когда разрешена все общая информация) вы не можете точно знать, что класс параметра метода будет и, следовательно, не может быть выведен. Объявления переменных хороши и постоянны, поэтому вы можете.
Кто-то еще может предоставить более подробную информацию и/или хорошую ссылку.: -)
В любом случае вы всегда можете указать параметры типа явно для общих вызовов, например:
Collections.<Integer>emptySet();
или даже несколько параметров одновременно, например
Collections.<String, Boolean>emptyMap(); // Returns a Map<String, Boolean>
Это часто выглядит немного чище, чем бросать, в тех случаях, когда вывод не срабатывает.
Ответ 2
попробуйте
FilterList emptyList = new FilterList(Collections.<Integer>emptySet());
Вы можете принудительно ввести параметр типа для методов, которые имеют их, в случаях, когда вывод недостаточно хорош, или чтобы вы могли использовать подтипы; например:
// forces use of ArrayList as parameter instead of the infered List
List<String> l = someObject.<ArrayList<String> methodThatTakesTypeParamForReturnType();
Ответ 3
Вы хотите сделать это:
FilterList emptyList = new FilterList(java.util.Collections.<Integer>emptySet());
Это указывает методу emptySet
, что его общий параметр должен явно указывать на Integer
вместо стандартного Object
. И да, синтаксис полностью фанковый и неинтуитивный для этого.:)
Ответ 4
У Java есть тип вывода, он довольно ограничен. Если вы заинтересованы в том, чтобы точно знать, как это работает и каковы его ограничения, это действительно приятно читать:
http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html#Type%2BArgument%2BInference