Kotlin - Невозможно создать два конструктора с разными параметрами типа List - PullRequest
2 голосов
/ 30 мая 2019

Я пытался создать следующий класс:

class MyClass {

    var foos: List<Foo> = listOf()

    constructor(foos: List<Foo>) {
        this.foos = foos
    }

    constructor(bars: List<Bar>) : super() {
        this.foos = bars.map { bar ->
            Foo(bar)
        }
    }
}


Однако я получаю сообщение об ошибке:

Столкновение объявления платформы: Следующие объявления имеютодна и та же сигнатура JVM ((Ljava / util / List;) V):


Я понимаю, что они оба являются объектами List, но они напечатаны с использованием обобщений, поэтому я был уверен, что это не будетпроблема.

Ответы [ 2 ]

2 голосов
/ 31 мая 2019

Вы столкнулись с этой проблемой, потому что в Java существует нечто, называемое Тип стирания . И поскольку Kotlin использует JVM, на это также влияет это ограничение. Чтобы дать TL; DR;

Универсальный тип сохраняется в файле .class, поэтому java знает, что класс (в вашем случае List) является универсальным. Но он не может отслеживать общий тип экземпляра. Таким образом, экземпляры List<Foo> и List<Bar> обрабатываются в форме raw type во время выполнения (List). Помните, что дженерики используются только во время компиляции для обеспечения безопасности типов.

Чтобы преодолеть это ограничение, вы можете использовать перегрузку операторов в kotlin. Оператор, на который мы смотрим, это (), который позволяет вам вызывать любой экземпляр. Используя companion object, мы можем даже сделать это invoke выглядит как конструктор и вызывается как один (MyClass()). Ваш код может выглядеть так:

class MyClass(var foos: List<Foo>) {
    companion object {
        operator fun invoke(bars: List<Bar>) = MyClass(bars.map(::Foo))
    }
}

Что позволяет вам назвать это просто так:

val mc1 = MyClass(foos) // calls constructor
val mc2 = MyClass(bars) // calls companion.invoke
1 голос
/ 30 мая 2019

Это не проблема Котлина, а скорее механизм на обобщениях Java.Этот механизм предназначен для того, чтобы избежать конфликтов в унаследованном коде, который все еще использует необработанные типы.

Например, если вы создадите в Java такой класс:

public class MyClass {

    private List<Foo> foos;

    MyClass(List<Foo> foos) {
        this.foos = foos;
    }

    MyClass(List<Bar> bars) {
        List<Foo> foos = new ArrayList<>();
        bars.forEach(bar -> foos.add(new Foo(bar)));
        this.foos = foos;
    }
}


Вы будетеполучить ошибку времени компиляции, говоря:

'MyClass (List)' предложения с 'MyClass (List)';оба метода имеют одно и то же стирание


То, что стирает тип, обеспечивает принудительное ограничение типа только во время компиляции и отбрасывание информации о типе элемента во время выполнения.Параметры типа в классе отбрасываются во время компиляции кода и заменяются его первой границей или Object, если параметр типа не связан.Следовательно, оба конструктора (поскольку они оба не ограничены) будут иметь List, следовательно, ошибка.

...