Inferring параметр типа класса по умолчанию

У меня есть класс с конструктором объявлен так

class Facade<T : SuperClass>(
    val kClass: KClass<in T> = SuperClass::class
)

Это делается так, чтобы разработчику не нужно было указывать SuperClass, если он хочет использовать его вместо подкласса. Причина даже для отправки типа класса такова, что разработчику не нужно указывать тип в угловых скобках.

Но теперь приходит проблема. Создание экземпляра, как показано ниже, говорит о том, что для вывода параметра T недостаточно информации, что приводит к необходимости помещать класс в угловые скобки.

Facade()

Но так как значением по умолчанию является SuperClass, то kotlin должен иметь возможность выводить параметр T как SuperClass. Что я не так думаю?

Спасибо

TL;DR:

Facade(SubClass:class) // working
Facade(SuperClass:class) // working, but don't want (superclass is default)
Facade<SuperClass>() // working, but don't want angle brackets <>
Facade() // not working, cannot infer T type from default, why?

2 ответа

Решение

Для Facade() быть эквивалентным Facade(DerivedClass::class) параметр конструктора по умолчанию должен быть объявлен как val kClass: KClass<in T> = T::class, Однако использовать T::class T параметр типа должен быть reified, Параметр типа может быть реализован только во встроенных функциях, но не в конструкторах.

Чтобы обойти эту проблему, вы можете объявить фабричную функцию, которая делегирует конструктору, вот так:

inline fun <reified T : SuperClass> Facade(): Facade<T> = Facade(T::class)

Это позволяет использовать его как, например:

val derivedFacade:Facade<DerivedClass> = Facade()

Обратите внимание, что если вы хотите использовать SuperClass в качестве параметра по умолчанию для T вам нужно будет объявить другой метод фабрики, используя другое имя, например:

fun SuperFacade(): Facade<SuperClass> = Facade()

Это необходимо, так как если бы мы объявили @JvmName("SuperFacade") fun Facade() = Facade(SuperClass::class) компиляция будет соответствовать ему всякий раз, когда мы не предоставляем параметры типа. Это, в свою очередь, не поддается выводу типа из derivedFacade пример.

Вы можете решить свою проблему, сняв угловые скобки и изменив тип конструктора. Просто используйте этот конструктор:

class Facade(val kClass: KClass<*> = SuperClass::class)

Все эти звонки работают

Facade(SubClass:class)
Facade(SuperClass:class)
Facade()
Другие вопросы по тегам