Kotlin и Java PECS - PullRequest
       29

Kotlin и Java PECS

0 голосов
/ 28 апреля 2020

Так что моя цель - использовать в Kotlin конструкцию, аналогичную Java PECS:

List<? extends MyMarkerInterface> => MutableList<out MyMarkerInterface>

Когда Джексон установил после компиляции данных в эту переменную (список), все в порядке. Когда я пытаюсь добавить элемент из Kotlin кода, Kotlin говорит, что я могу добавить только элементы Nothing (type).

Так как мне поместить дочерний элемент List в MyMarkerInterface в Kotlin?

Ответы [ 2 ]

0 голосов
/ 28 апреля 2020

Это примерно дисперсия .

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

Предположим, у вас есть ссылка на MutableList<out MyMarkerInterface>. Этот параметр эквивалентен Java <? extends MyMarkerInterface> и означает, что у вас есть изменяемый список MyMarkerInterface или некоторый подтип . Но вы не знаете , какой подтип; это может быть любой тип, реализующий ваш интерфейс.

Это может быть изменяемый список MyImplementingClassA; таким образом, вы явно не можете добавить экземпляр MyImplementingClassB, не рискуя нарушить его безопасность типов. Ни наоборот. На самом деле, не зная больше о его типе, небезопасно добавлять что-нибудь к этому . Вот почему Kotlin не позволит вам. (Это делается путем вывода типа Nothing, который является «нижним» и не имеет значений.)

Однако, если вы хотите получить значение из списка, вы знаете, что это какой-то подтип MyMarkerInterface, поэтому вы можете с радостью рассматривать его как MyMarkerInterface ссылку.

Вот почему out дисперсия означает список продюсер : он может создавать значения для вас безопасно, но не может использовать значения, потому что ни один тип не будет безопасным.

И это полная противоположность для in дисперсия, которая эквивалентна Java ? super.

(что, конечно, составляет PECS . Это точное выражение применимо только к Java - Kotlin эквивалент - «Producer Out, Consumer In», что, вероятно, слишком очевидно, чтобы использовать аббревиатуру! - но принцип тот же.)

Единственный безопасный способ быть обоими продюсерами и потребитель должен быть инвариантом : обычный старый MutableList<MyMarkerInterface>. Таким образом, вы знаете, что вы можете получить MyMarkerInterface значения из и положить MyMarkerInterface значения в.

Вещи разные для List s, которые неизменны в Kotlin , (Или, скорее, вы не можете изменить их через эту ссылку ; возможно, вы сможете каким-то другим способом.) Поскольку эта ссылка не позволит вам ввести значения, a List не выступает в роли потребителя, только производитель, поэтому для этого вполне подойдет out дисперсия .

-

Ваш вопрос не дает никаких подробностей, но вы упомянули инициализацию , Вероятно, наиболее распространенным является создание List со всеми значениями, которые он будет иметь заранее - либо путем вызова listOf(), либо с помощью конструктора, либо в результате map() или аналогичная операция.

Однако вы также можете создать некоторый тип MutableList, настроить его по мере необходимости, а затем увеличить его до List (который является суперинтерфейсом MutableList), например:

val ml = ArrayList<MyImplementingClassA>()
// …some computation which calls ml.add()…
val l = ml as List<out MyMarkerInterface>

… хотя на практике вам обычно не нужна эта последняя строка; Kotlin знает , что List s имеют out дисперсию, и поэтому при необходимости он автоматически повышается с ml до List<out MyMarkerInterface>.

0 голосов
/ 28 апреля 2020

Удалять вообще, и работает нормально, но без "? Extends", когда декомпилирует код из байт-кода после Kotlin

...