Значение слов «in» и «out» в дженериках Kotlin

Я изучаю дженерики Kotlin, но именование сводит меня с ума.

Давайте рассмотрим два фрагмента кода на Java и Kotlin, которые выполняют одну и ту же работу:

1. Ява

    public void copy(List<? extends Number> from, List<? super Number> to) {
        assert from.size() == to.size();
        for (int i = 0; i < from.size(); ++i) {
            to.add(from.get(i));
        }
    }

Все ясно.

from — это список элементов типа, наследуемого от Number. Так что это может быть List<Double>List <Short> и т. д. Я понимаю, почему используется слово расширяется.

to — это список элементов типа, который является супертипом Number. Итак, это could List<Number> или List<Serializable> или List<Object>

2.Котлин

Аналогичный код для Kotlin:

    fun copy(from: Array<out Number>, to: Array<in Number>) {
        assert(from.size == to.size)
        for (i in from.indices)
            to[i] = from[i]
    }

С моей точки зрения это выглядит нелогично. Почему Array<out Number> — это массив подтипов числа?

Почему Array<in Number> Массив супертипов Number?

Я уверен, что у создателей была какая-то логика, и я хочу ее понять.

Думаю, это дело вкуса. Я, пользуясь Java в течение 15 лет, до сих пор не могу понять, какую из них использовать. Я это понимаю, мне просто нужно подумать дважды, третий раз и четвертый раз, действительно ли то, что я делаю, мне нужно. in/out для меня было типа: «о, так это может быть ТАК просто?».

broot 13.08.2024 18:13
kotlinlang.org/docs/generics.html
Asmir 13.08.2024 18:20

Java PECS: Производитель расширяет, Потребитель — супер. Kotlin in/out: out — это то, что производит вещи, из которых вы можете что-то получить; in — это то, куда можно положить вещи.

David Conrad 13.08.2024 20:09
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
3
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Array<out Number> — это массив, из которого можно получить Number.

Array<in Number> — это массив, в который можно поместить Number.

В этом суть того, как вам следует об этом думать, и, в частности, почему эти имена имеют смысл. Что именно являются соответствующими типами - когда вы думаете о том, «из какого массива я могу получить Number», вы заметите, что вы, безусловно, можете получить Number из Array<Int> или Array<Double>, и заключите, что любой тип, который расширяет Number является допустимым аргументом типа для Array<out Number>. Кроме того, вы, конечно, можете поместить любой Number в Array<Object> и сделать вывод, что Array<in Number> — это Array<T>, где T — это некоторый супертип Number.

Однако в целом Котлин просто переключается с вопроса «что это может быть за тип?» что List<? extends Number> проясняет вопрос «что я могу сделать с этим типом?» что MutableList<in Number> проясняет.

Это немного ломает мне мозг, но, похоже, отвечает на мой вопрос.

gstackoverflow 13.08.2024 18:16

Добавляем к великолепному ответу @Louis Wasserman и объясняем, почему это изменилось:

Если вам когда-нибудь понадобится описать параметры вашей функции copy своему другу, вы бы лучше сказали:

Мне нужен один массив чисел для чтения и другой массив чисел для записи.

Или что-то вроде:

Мне нужен один массив чисел или их подклассы и другой массив чисел или их суперклассы.

Сначала звучит как разговор с человеком. Второе звучит как разговор с компилятором.

Лично для меня extends/super ощущаются как результат моего дизайнерского выбора. in/out — это сам выбор дизайна.

Одна вещь, которая может потенциально сбить с толку в отношении in/out, заключается в том, что в зависимости от нашей точки зрения мы можем утверждать, что in должно быть для ковариантности, а out для контравариантности. Неоднозначность возникает из-за того, что мы можем взглянуть на это с двух разных сторон общения. Например: «Мы потребляем предметы из этого объекта» (InputStream). «Этот объект потребляет предметы» (вариант in).

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