Kotlin 通用工厂

Kotlin generic factories

我正在尝试创建一个 AnimalFactory returns 通用工厂,用于制作不同类型的 Animal,具体取决于传递给 AnimalFactory 的参数。

代码如下:

interface Animal {
    fun talk(): String
}

class Cow: Animal {
    override fun talk(): String {
        return "mooo"
    }
}

class Cat: Animal {
    override fun talk(): String {
        return "miow"
    }
}

class Dog: Animal {
    override fun talk(): String {
        return "bark"
    }
}

object AnimalFactory {
    fun <T: Animal> AnimalMakerFactory(type: String): AnimalMaker<T> {
        val maker = when (type) {
            "cat" -> CatMaker()
            "dog" -> DogMaker()
            else -> CowMaker()
        }
        
        return maker
    }
}

interface AnimalMaker<out T: Animal> {
    fun make(): T
}

class CatMaker: AnimalMaker<Cat> {
    override fun make(): Cat {
        return Cat()
    }
}

class DogMaker: AnimalMaker<Dog> {
    override fun make(): Dog {
        return Dog()
    }
}

class CowMaker: AnimalMaker<Cow> {
    override fun make(): Cow {
        return Cow()
    }
}

我遇到类型异常:

Type mismatch.
Required: AnimalMaker<T>
Found: AnimalMaker<Animal>

我以为 AnimalMaker 会解决这个问题,但显然不是。为什么 AnimalMaker<T> 不是 AnimalMaker<Animal> 类型?

函数的 return 值是 AnimalMaker<T> 而不是 AnimalMaker<Animal> 因为那是你声明的 return 类型。变量 maker 确实是一个 AnimalMaker<Animal> 但这与函数应该 return 不匹配,因为 T 可能是 Animal.

的子类型

您将您的函数声明为具有 T: Animal 的泛型类型。通用类型始终是函数的输入。在这种情况下,使用函数的通用输入是没有意义的,因为没有办法强制给定的类型与它对应的输入字符串相匹配。要使您的功能正常工作,您可以删除 <T : Animal 并声明它 returns AnimalMaker<Animal>.

多一点解释。您可能希望在函数签名中使用泛型的原因有两个。

  1. 强制输入参数类型。
  2. 确定输出类型。

您可能出于一个或两个原因使用泛型(但第二个原因只能通过使用具体化的泛型以有用的方式自行完成,除非在非常特殊的情况下 returned class 不会产生任何东西)。

在您的情况下,您的输入泛型不用于强制输入参数,因为它只是一个字符串。要出于第二个原因使用它,您必须将 return 值的类型转换为未知的(对于编译器)类型 T 这将是不安全的,因为无法知道调用站点给出的输入类型是否是给定输入字符串的有效匹配。如果您希望调用站点传递正确的类型,这将是多余的并且容易出错,还需要传递匹配的字符串。

编辑: 如果您在编译时知道输入类型,那么您可以使用具体化的泛型来做到这一点。去掉 String 输入。它看起来像这样:

object AnimalFactory {
    inline fun <reified T: Animal> AnimalMakerFactory(): AnimalMaker<T> {
        @Suppress("UNCHECKED_CAST")
        return when (T::class) {
            Cat::class -> CatMaker()
            Dog::class -> DogMaker()
            Cow::class -> CowMaker()
            else -> error("No factory found for type ${T::class}.")
        } as AnimalMaker<T>
    }
}

// Example usage
val someCatFactory = AnimalFactory.AnimalFactoryMaker<Cat>()
val cat: Cat = someCatFactory.make()

在此函数内,是否正确匹配类型取决于您,否则在运行时将出现 ClassCastException。从逻辑上讲它应该能够自动转换它们,但编译器还不够复杂(还?)。