Kotlin: вернуть общий интерфейс

В Котлин я пытаюсь скомпилировать следующее:

  1. учитывая интерфейс с универсальными типами (принтер)
  2. и две реализации этого интерфейса (DogPrinter и CatPrinter)
  3. верните один из принтеров в соответствии с внешней переменной (AnimalType)

Следующий код не компилируется, потому что требуется тип: fun getMapper(animalType: AnimalType): Printer

Я пытался использовать <Any> или <*> но не добился успеха. Может кто-нибудь помочь?

(легко увидеть ошибку, скопировав код ниже на https://try.kotlinlang.org)

 enum class AnimalType { CAT, DOG } class Dog class Cat interface Printer<in T> { fun mapToString(input: T): String } class DogPrinter : Printer<Dog> { override fun mapToString(input: Dog): String { return "dog" } } class CatPrinter : Printer<Cat> { override fun mapToString(input: Cat): String { return "cat" } } private fun getMapper(animalType: AnimalType): Printer { return when(animalType) { AnimalType.CAT -> CatPrinter() AnimalType.DOG -> DogPrinter() } } fun usage_does_not_compile() { getMapper(AnimalType.DOG) .mapToString(5) } 

Я немного изменил код. getMapper функция getMapper в общий тип getMapper , что делает вызов довольно читаемым в getMapper<Dog>() .

Читайте о reified здесь: Что действительно делает ключевое слово reified в Kotlin?

 private inline fun <reified T> getMapper(): Printer<T> { when (T::class) { Cat::class -> return CatPrinter() as Printer<T> Dog::class -> return DogPrinter() as Printer<T> } throw IllegalArgumentException() } fun main(args: Array<String>) { println(getMapper<Dog>().mapToString(Dog())) println(getMapper<Cat>().mapToString(Cat())) } 

Оверенная вещь даже не нужна на самом деле, но делает клиентскую сторону более читаемой. Кроме того, вы также можете передать класс в качестве аргумента функции getMapper . Действительно важная часть состоит в том, чтобы сделать этот общий . Непроверенные броски не очень круты, но, похоже, здесь безопасны.

У Kotlin нет сырых типов, поэтому вы не можете вернуть Printer из getMapper . Вы можете вернуть Printer<*> , сделать getMapper generic или изменить структуру наследования, чтобы иметь общий супер-тип.

Возвращаемый Printer<*> работает нормально. Но на самом деле это не полезно, потому что вы не можете вызвать mapToString(Dog()) (я предполагаю, что mapToString(5) – это опечатка), и это хорошо: если он скомпилирован тогда

 getMapper(AnimalType.CAT).mapToString(Dog()) 

также должен был бы скомпилироваться, поскольку тип возвращаемого значения может зависеть только от типа аргумента, а не от значения , а AnimalType.CAT и AnimalType.DOG имеют тот же тип. Есть некоторые языки, которые позволяют это, но Котлин не является одним из них.