【问题标题】:Create abstract class with generic Type in Kotlin在 Kotlin 中创建具有泛型类型的抽象类
【发布时间】:2020-10-06 23:24:35
【问题描述】:

我很困惑,我一直在寻找 kotlin 泛型是如何工作的,但我完全不明白。

基本上,我有BaseAnimalDetail 类,它与一些扩展Animal 类的类一起使用。目前BaseAnimalDetail类中有很多when,所以我的目标是避免所有那些when,只是扩展BaseAnimalDetail whenever I need, for each class that extends Animal`

open class BaseAnimalDetail<T : Animal?> : LinearLayout{
    private var animal: T? = null
    
    fun setAnimal(animal:T){
        this.animal = animal
    }

    private fun play() {
        when(animal){
           is Horse -> playWithHorse...
           is Dog -> playWithDog...
           is Cat -> playWithCat...
        }
        animal?.let { it.play() } //this is not working but animal?.play works fine, WHY?
    }
    ...More code
}

如您所见,when 超级丑陋,所以我想将BaseAnimalDetail 抽象化并按照我说的那样实现它。

上面的代码只是一个例子,但它可以帮助我理解泛型在 Kotlin 中是如何工作的。我必须在基类中有 T var

此外,为什么animal?.let { it.play() } 无法编译?

【问题讨论】:

  • 你能发布你的动物课程吗?

标签: android kotlin generics


【解决方案1】:

在您描述的情况下,您希望每种类型的 Animal 都有自己的 BaseAnimalDetail 子类。所以你应该让 BaseAnimalDetail 成为一个抽象类,然后你可以为每个 Animal 创建子类。

顺便说一句,由于 Kotlin 有属性,所以将 animal 设为私有并具有 setter 函数是多余的。

abstract class BaseAnimalDetail<T : Animal> {
    var animal: T? = null
    abstract fun play()
}

class CatDetail: BaseAnimalDetail<Cat>(){
    override fun play() {
        println("toss yarn to $animal")
    }
}

但是,如果所有这些不同的功能对于每种类型的 Animal 都不同,那么在我看来,您根本不应该有一个单独的 BaseAnimalDetail 类,它的功能应该只存在于各种 Animal 类中。或者,如果存在某些动物共有的某些行为,您可以创建各种可以添加到 Animal 类的行为类(组合设计策略)。

【讨论】:

  • 我不明白你为什么要创建那个构造函数。我有一个 setAnimal 方法可以在需要时设置它。我必须让 var animal T 在需要时从 BaseAnimalDetail 使用它
  • 该属性不必在构造函数中(这只是为了方便),但 setAnimal 在 Kotlin 中是完全多余的,因为 animal 是一个属性。它有一个带有支持字段的隐含 getter 和 setter。您可以稍后在声明站点自定义 setter,而无需更改任何签名,就像使用 Java setter 一样。
  • 我更新了它以将属性拉出构造函数。不过,这与您的要求并不相关,是吗?
  • 我突然想到,也许您不知道构造函数中的属性与构造函数之外的属性一样有效。它们不是构造函数私有的,但可以在对象的生命周期内使用,就像其他属性一样。将它们放在构造函数中可以方便地在实例化类时设置它们。
【解决方案2】:

不确定是否正确理解你,但你能做的就是如你所说 - 将你的 BaseAnimalDetail 转换为具有抽象乐趣的抽象类 fun play() 并让 Animal 类扩展 BaseAnimalDetail 或让你的 @987654326 @, Dog 直接扩展。

abstract class BaseAnimalDetail {
    abstract fun play()
}

open class Animal : BaseAnimalDetail() {
    override fun play() = Unit
}

class Horse : Animal() {

    override fun play() {
        playWithHorse()
    }

    private fun playWithHorse() {}
}

class Dog : BaseAnimalDetail() {

    override fun play() {
        playWithDog()
    }

    private fun playWithDog() {}
}

也可以通过接口提供“每当我需要时”,因为我们的目标是提供一个功能,并且那里没有更深层次的逻辑。

【讨论】:

  • 必须让var animal T 在需要时从BaseAnimalDetail 使用它,此外,你知道为什么animal?.let { it.play() } 无法编译吗?谢谢
  • animal?.let { it.play() } 的错误是什么?看起来它应该可以正常工作,虽然它只是写 animal?.play() 的冗长方式。
  • 类型推断失败。预期类型不匹配:必需:发现动物:T!
  • 我认为您已经删除了太多关于它周围发生的事情的上下文,以便我们看到问题所在。你上面的工作正常:pl.kotl.in/hvAe2OnNy
  • 如果play函数有返回类型,就会出现这个错误。
【解决方案3】:

不知道为什么需要泛型,这是我的实现

abstract class Animal {
    abstract fun play()
}

class Horse : Animal() {
    override fun play() {
        playWithHorse()
    }
    private fun playWithHorse() {}
}

class Dog : Animal() {
    override fun play() {
        playWithDog()
    }
    private fun playWithDog() {}
}


open class BaseAnimalDetail(var animal: Animal?=null) {
    private fun play() {
       animal?.play()
    }
}

class HorseDetail(var horse: Horse? = null): BaseAnimalDetail(horse){

}

您可以从 BaseAnimalDetail 中删除 play func,也可以直接调用 horse?.play()

【讨论】:

  • 我的代码只是我需要的更复杂的示例,我需要在 BaseAnimalDetail 中有一个变量(而不是在构造函数中)。谢谢
  • 它是默认参数,所以你不需要在构造函数中传递值。
  • 如果您想检索 animal 并让它自动被编译器视为已知子类型,则需要泛型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多