【问题标题】:Kotlin type system - how to "add" a property to subclasses of two related classesKotlin 类型系统 - 如何将属性“添加”到两个相关类的子类
【发布时间】:2019-08-11 04:10:03
【问题描述】:

我有两个库类,ItemItemFood : Item(派生自 Item)和一个库函数 registerItem(item: Item, name: String)。我无法修改它们。

我有两个派生自库类的自己的类(ItemKey : ItemItemBerry : ItemFood)。

我想要的是将name: String 属性存储在我的类ItemKeyItemBerry 中,并让它们“计数”为NamedItem,这样我就可以编写这样的函数:

fun registerNamedItem(namedItem: NamedItem) {
    registerItem(namedItem, namedItem.name)
}

我不能像这样创建一个类:class NamedItem(val name: String) : Item 并从中派生我的类,因为有时我需要从 ItemFood 而不是 Item 派生我的类。

我不想制作像class NamedItem(val item: Item, val name: String) 这样的类包装器,因为每次我想获取“底层”Item 时,我都需要手动获取item 属性:registerItem(namedItem.item, namedItem.name),并且这很丑。

我不能使用interface INamedItem { val name: String } 并在ItemKeyItemBerry 中实现这个接口,因为那样我就需要这样写一个函数:

fun registerNamedItem(item: Item, namedItem: INamedItem) {
    registerItem(item, namedItem.name)
}

,这根本不是改进。

是否有某种高级技术 - 使用接口、委托、泛型等等 - 这样我就可以实现我想要的 registerNamedItem 函数 - 将 NamedItem 的实例传递给 registerItem(item: Item, name: String) 作为第一个参数和namedItem.name作为第二个参数?

【问题讨论】:

  • Item 是类还是接口?
  • 优先组合而不是继承。 “我不想制作一个类包装器,因为每次我想要获取底层 Item 时,这都很难看”这不是一个有效的论点,尤其是在 Kotlin 中,您有内置的按委托实现支持。
  • 其实你的情况正是我们不喜欢继承的主要原因之一。
  • 2Alexey Romanov: Item 是一个类,并且有一个派生类ItemFood
  • 2SOFe:如何利用委托来消除在函数中使用namedItem.item 的丑陋?另外,我工作的框架迫使我继承Item/ItemFood 来添加新的项目,所以我别无选择。

标签: kotlin


【解决方案1】:

实际上,你可以使用 interface 来实现这一点:

fun main() {
    registerNamedItem(ItemKey("item_key"))
    registerNamedItem(ItemBerry("item_berry"))
}

// Cannot change this
open class Item

// Cannot change this
open class ItemFood : Item()

// This is your class
class ItemKey(override val name: String) : NamedItem()

// This is also your class
class ItemBerry(override val name: String) : NamedFoodItem()

// This is the property you would like to enforce
interface INamedItem {
    val name: String
}

// Since Item and ItemFood are concrete classes, you don't have much choice there
abstract class NamedItem : Item(), INamedItem    
abstract class NamedFoodItem : ItemFood(), INamedItem

// Adapter pattern
fun registerNamedItem(namedItem: NamedFoodItem) {
    registerItem(namedItem, namedItem.name)
}

// Adapter pattern
fun registerNamedItem(namedItem: NamedItem) {
    registerItem(namedItem, namedItem.name)
}

fun registerItem(namedItem: Item, name: String) {
    println("Item $namedItem registered with $name")
}

委托在您的情况下不起作用,因为在您的示例中,Item 是一个类,您只能委托给接口。

【讨论】:

  • 问题是registerItem框架函数需要Item,而不是NamedItem,所以你不能给它一个NamedItem类型的变量
  • 请记住,我没有您的代码,也不知道您使用的是什么“框架”。更新了我的例子。但实际上,也许你的反思会更好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多