Вариант использования сайта в Котлине - PullRequest
0 голосов
/ 05 ноября 2018
open class A
class B: A()

fun <T> copy(src: MutableList<T>, dst: MutableList<T>) {
    for (i in 0 until src.size) {
        dst.add(i, src[i])
    }
}

Для вышеупомянутого кода я понимаю, что copy function ожидает, что оба типа параметров точно такого же типа . С небольшой модификацией copy(src: MutableList<T>, dst: MutableList<in T>) обратите внимание на ключевое слово in , я говорю, что src должно быть точно типа T, но пункт назначения может быть type T или любой супер тип Т .

Для вышеуказанного измененного кода я могу вызвать метод следующим образом:

fun main(args: Array<String>) {
    val l1 = mutableListOf(B(), B())
    val l2 = mutableListOf<A>()
    copy(l1, l2)
} // main

Вышеуказанное copy(l1, l2) не работает, если я удаляю in из пункта назначения (понятно).

У меня вопрос, я могу вызвать функцию без ошибок , если обновить параметр функции src, чтобы принять out проекцию списка . например, * * одна тысяча тридцать две

fun <T> copy(src: MutableList<out /*notice out here*/ T>, dst: MutableList<T>) {
    for (i in 0 until src.size) {
        dst.add(i, src[i])
    }
}

В этом случае я не могу понять, что происходит под капотом. Может кто-нибудь объяснить, пожалуйста?

Обратите внимание, что это только пример из книги. Я знаю, что могу использовать List вместо неизменяемого списка в src

Ответы [ 2 ]

0 голосов
/ 05 ноября 2018

out здесь работает симметрично in:

в ключевом слове я говорю, что src должен быть точно типа T, но destination может быть типа T или любого супер-типа T

Итак, теперь вы говорите, что src должно быть MutableList типа T или любым подтипом T, тогда как dst должно быть MutableList точно типа T.

Поэтому, когда у вас есть l1: MutableList<B> и l2: MutableList<A>, компилятор выводит параметр типа в copy(l1, l2) как copy<A>(l1, l2), и он проверяет тип: MutableList<B> является подтипом MutableList<out A>.

Поскольку вы используете только out -совместимые операции на src и только in -совместимые операции на dst, так как @ s1m0nw1 говорит, что имеет смысл включить оба модификатора.

0 голосов
/ 05 ноября 2018

, поскольку вы используете функцию только одним способом, вы должны в любом случае использовать модификаторы дисперсии use-site, чтобы дать понять вызывающей стороне, что вы можете добавить к dst и получить данные от src:

fun <T> copy(src: MutableList<out T>, dst: MutableList<in T>) {
    for (i in 0 until src.size) {
        dst.add(i, src[i])
    }
}

Далее, поскольку src действительно используется как List, а не MutableList, вы должны предпочесть его соответственно. В результате вам больше не понадобится модификатор out, поскольку List уже определил параметр типа T только как out:

fun <T> copy(src: List<T>, dst: MutableList<in T>)

В ответ на ваш вопрос: На самом деле проблема возникает, когда вы вызываете copy с двумя списками разного типа в главном, один раз с MutableList<A> и один раз с MutableList<B>. Компилятор не может определить, должен ли тип copy быть A или B. Чтобы это исправить, вам нужно дать больше информации:

1) Когда вы устанавливаете dst на MutableList<in T>, компилятор знает, что вы будете добавлять к нему только T типов на основе src (в вашем примере это B).

2) Когда вы устанавливаете src на MutableList<out T>, компилятор понимает, что вы добавите только T и его подтипы к dst, что тоже хорошо (в этом случае T будет выведено как A хотя).

...