Вывод параметров общего типа в цепочке методов

Прочитав этот вопрос, я начал думать об общих методах в 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 для вывода на работу и, насколько я могу судить, это будет очень трудно сделать (помимо того, что очень сложный).

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

Другие вопросы по тегам