Вывод параметров общего типа в цепочке методов
Прочитав этот вопрос, я начал думать об общих методах в Java 8. В частности, что происходит с параметрами универсального типа, когда методы объединены в цепочку.
Для этого вопроса я буду использовать некоторые общие методы из Гуавы ImmutableMap
, но мой вопрос носит более общий характер и может быть применен ко всем связанным цепочечным методам.
Рассматривать ImmutableMap.of
универсальный метод, который имеет эту подпись:
public static <K, V> ImmutableMap<K, V> of(K k1, V v1)
Если мы используем этот универсальный метод для объявления Map
компилятор правильно выводит универсальные типы:
Map<String, String> map = ImmutableMap.of("a", "b");
Я знаю, что начиная с Java 8, механизм вывода компилятора был улучшен, то есть он выводит универсальные типы методов из контекста, который в данном случае является присваиванием.
Контекст также может быть вызовом метода:
void someMethod(Map<String, String> map) {
// do something with map
}
someMethod(ImmutableMap.of("a", "b"));
В этом случае общие типы ImmutableMap.of
выводятся из общих типов аргумента someMethod
т.е. Map<String, String> map
,
Но когда я пытаюсь использовать ImmutableMap.builder()
и цепочка методов для построения моей карты, я получаю ошибку компиляции:
Map<String, String> map = ImmutableMap.builder()
.put("a", "b")
.build(); // error here: does not compile
Ошибка:
Error:(...) java: incompatible types:
ImmutableMap<Object, Object> cannot be converted to Map<String, String>
(Я убрал имена пакетов из сообщения об ошибке для ясности).
Я понимаю ошибку и почему это происходит. Первый метод в цепочке ImmutableMap.builder()
и у компилятора нет контекста для вывода параметров типа, поэтому <Object, Object>
, Затем ImmutableMap.Builder.put
метод вызывается с аргументами "a"
а также "b"
и наконец ImmutableMap.Builder.build()
вызывается метод, который возвращает ImmutableMap<Object, Object>
, Вот почему я получаю ошибку несовместимых типов: когда я пытаюсь назначить это ImmutableMap<Object, Object>
экземпляр моего Map<String, String> map
переменная, жалуется компилятор.
Я даже знаю, как решить эту ошибку: я мог бы разбить цепочку методов на две строки, чтобы компилятор теперь мог выводить параметры универсального типа:
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
Map<String, String> map = builder.put("a", "b").build();
Или я мог бы явно предоставить параметры универсального типа:
Map<String, String> map = ImmutableMap.<String, String>builder()
.put("a", "b")
.build();
Поэтому мой вопрос не в том, как решить / обойти это, а в том, почему мне нужно явно указывать параметры универсального типа при объединении в цепочку универсальных методов. Или, другими словами, почему компилятор не может вывести параметры универсального типа в цепочке методов, особенно когда цепочка методов находится справа от присваивания? Если бы это было возможно, это нарушило бы что-то еще (я имею в виду, связанное с системой типов обобщенных типов)?
РЕДАКТИРОВАТЬ:
Есть вопрос, задающий то же самое, однако единственный ответ, который у него есть, не ясно объясняет, почему компилятор не выводит параметры универсального типа в цепочке методов. Все, что у него есть, - это ссылка на небольшой абзац в лямбда-выражениях JSR-000335 для финального выпуска языка программирования JavaTM для оценки (часть спецификации D):
Был некоторый интерес к разрешению вывода для "цепочки": в a(). B (), передача информации о типе от вызова b к вызову a. Это добавляет еще одно измерение к сложности алгоритма вывода, так как частичная информация должна проходить в обоих направлениях; это работает только тогда, когда стирание возвращаемого типа a () фиксировано для всех экземпляров (например, List). Эта функция не очень хорошо вписывается в модель выражения поли, поскольку целевой тип не может быть легко получен; но, возможно, с дополнительными улучшениями он может быть добавлен в будущем.
Поэтому я думаю, что я мог бы перефразировать мой первоначальный вопрос следующим образом:
- Какими будут эти дополнительные улучшения?
- Как получается, что передача частичной информации в обоих направлениях сделает алгоритм вывода более сложным?
- Почему это работает только тогда, когда стирание возвращаемого типа a () фиксируется для всех экземпляров (например, List)? На самом деле, что это означает, что стирание возвращаемого типа метода является фиксированным для всех экземпляров?
- Почему эта функция не очень хорошо вписывается в модель выражения поли? На самом деле, что такое модель поли выражения? И почему целевой тип не может быть легко получен в этом случае?
1 ответ
Если это действительно комментарий, пожалуйста, дайте мне знать, и я разбью его на отдельные комментарии (вероятно, они не будут вписываться в один).
Первый poly expression
это тот, который может иметь разные типы в разных контекстах. Вы заявляете что-то в contextA
она имеет typeA
; один в contextB
и это имеет typeB
,
Что вы делаете с созданием вашей карты через ImmutableMap.of("a", "b")
или же ImmutableMap.of(1,2)
это такая вещь. Чтобы быть более точным, в главе 15.2 в JLS говорится, что на самом деле это поли-выражение выражения создания экземпляра класса.
Итак, мы установили, что A generic class instance creation is a poly expression
, Таким образом, создание экземпляра может иметь разные типы в зависимости от контекста, в котором он используется (и это, очевидно, происходит).
Теперь в вашем примере с ImmutableMap.builder
вещи не так сложно вывести (если бы вы могли builder
а также of
например) Строитель объявлен так:
public static <K, V> Builder<K, V> builder() {
return new Builder<K, V>();
}
а также ImmutableMap.of
как это:
public static <K, V> ImmutableMap<K, V> of() {
return ImmutableBiMap.of();
}
Обратите внимание, как оба объявляют одни и те же универсальные типы K,V
, Это, вероятно, будет легко перейти от одного метода к другому. Но рассмотрим случай, когда ваши методы (let's say 10 methods
) каждый объявляет различные границы универсальных типов, например:
<K,V> of()
....
<M extends T, R extends S> build()
И так далее. И вы можете связать их. Информация о типах должна быть передана от left to right
и из right to left
для вывода на работу и, насколько я могу судить, это будет очень трудно сделать (помимо того, что очень сложный).
Прямо сейчас этот способ работает так, что каждое поли-выражение компилируется по одному из того, что я вижу (и ваши примеры, кажется, доказывают это).