Kotlin: ссылка на конструктор для параметризованного типа дает ошибку компиляции - PullRequest
0 голосов
/ 26 августа 2018

Я пытаюсь написать в Kotlin что-то вроде следующего кода Java:

interface Provider {
}

class ProviderImpl1 implements Provider {
}

class ProviderImpl2 implements Provider {
}

enum Providers {
    ONE(ProviderImpl1::new),
    TWO(ProviderImpl2::new);

    private final Supplier<Provider> supplier;

    Providers(Supplier<Provider> supplier) {
        this.supplier = supplier;
    }

    public Provider provider() {
        return supplier.get();
    }
}

Этот код компилируется и работает правильно: Providers.ONE создает экземпляр ProviderImpl1, а Providers.TWO даетэкземпляр ProviderImpl2.

Вот что я мог бы достичь в Kotlin:

interface Provider {
}

class ProviderImpl1 : Provider {
}

class ProviderImpl2: Provider {
}

enum class Providers(private val factory: Supplier<Provider>) {
    ONE(Supplier{ ProviderImpl1() }),
    TWO(Supplier{ ProviderImpl2() });

    fun provider(): Provider = factory.get()
}

Это работает, но в Java я могу использовать ссылку на конструктор в конструкторе перечисления,Когда я пытаюсь сделать то же самое в Kotlin, а именно

ONE( ::ProviderImpl1 ),

, я получаю следующую ошибку компиляции:

Несоответствие типов: предполагаемый тип - KFunction0, но ожидается, что поставщик

Лямбда без явного типа также не работает:

ONE( ::ProviderImpl1 )

дает

Несоответствие типов: выводимый тип is () -> ProviderImpl1, но ожидался поставщик

Вопрос в том, запрещает ли это спецификация Kotlin (и если да, то почему, как кажется, Java справляется с этим), или это всего лишь временное несовершенство текущего компилятора Kotlin?

My build.gradle имеет следующее

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.2.61'
}

Версия языка Kotlin отображается Idea (в настройках проекта) как 1.2.

Ответы [ 2 ]

0 голосов
/ 26 августа 2018

Вы можете использовать kotlin.reflect.KFunction0, как предлагает ошибка компиляции, вместо java.util.function.Supplier, а затем вы можете использовать ссылку на метод.

Пример:

import kotlin.reflect.KFunction0

interface Provider {
}

class ProviderImpl1 : Provider {
}

class ProviderImpl2: Provider {
}

enum class Providers(private val factory: KFunction0<Provider>) {
    ONE(::ProviderImpl1),
    TWO(::ProviderImpl2);

    fun provider(): Provider = factory.call()
}

В этом случае сообщение об ошибке предполагает, что он ожидает интерфейс kotlin.reflect.KFunction0, отличный от java.util.function.Supplier, поэтому в этом конструкторе нет запрета на использование ссылок на методы. Вы можете использовать его, вам просто нужно использовать ожидаемый интерфейс.

0 голосов
/ 26 августа 2018

Если вы измените свой Supplier на лямбду, это может быть достигнуто довольно просто в Kotlin ...

interface Provider
class ProviderImpl1 : Provider
class ProviderImpl2 : Provider

enum class Providers(private val supplier: () -> Provider) {
    ONE({ ProviderImpl1() }),
    TWO({ ProviderImpl2() });

    fun provider(): Provider = supplier.invoke()
}

Здесь необходимо передать функцию, которая возвращает экземпляр Provider(что, по сути, Suppiler).Это хорошо, потому что, если в будущем вашей Provider реализации потребуется какая-то конфигурация во время ее создания, эта лямбда может справиться с этим.

Если ваш провайдер не имеет состояния, вы можете изменить Providers.provider() на val, где он будет создаваться только один раз для каждого типа перечисления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...