kotlin,如何简化将参数传递给 base class 构造函数?
kotlin, how to simplify passing parameters to base class constructor?
我们有一个包,我们希望将其从 python 转换为 kotlin,以便能够使用该包迁移系统。
包中有一组 class 都是变体,或者 'flavours' 共同基础 class。
大部分代码都在基础 class 中,其中有大量可选参数。所以考虑:
open class BaseTree(val height:Int=10,val roots:Boolean=true, //......还有很多!!
class FruitTree(val fruitSize, height:Int=10, roots:Boolean=true,
// now need all possible parameters for any possible instance
):BaseTree(height=height, roots=roots //... yet another variation of same list
代码实际上不是树,我只是认为这是传达想法的一种简单方式。 base class 大约有 20 个参数,大约有 10 个 subclasses,每个 subclass 实际上需要从 base [=] 重复参数列表的相同两个变体40=]。如果参数列表发生变化,那将是一场真正的噩梦!
有Java背景的人可能会评论“20个参数太多了”,可能会忽略这是可选参数,语言特性这会影响设计的这一方面。 20 个必需参数会很疯狂,但是 10 个甚至 20 个可选参数并不少见,例如检查 sqlalchemy Table。
在 python 中,你调用一个基础 class 构造函数你可以有:
def __init__(self, special, *args, **kwargs):
super().__init(*args, **kwargs) # pass all parameters except special to base constructor
有没有人知道一种技术,使用不同的方法(可能使用接口或其他方法?)来避免为每个子class 一遍又一遍地重复此参数列表?
如果你的子类在构造函数中真的有那么多参数 -> 没办法。你需要全部通过。
但是(大多数情况下)这不是一个好兆头,constructor/function 有那么多参数...
你并不孤单。 gradle-slack 频道已经对此进行了讨论。也许在未来,我们会得到编译器的帮助,但现在,你需要自己传递参数。
您可以通过按顺序排列变量来跳过 BaseTree(height, roots)
这样的名称,但是您不能像 Python 这样的操作,因为 Python 是动态语言。
Java 也要把变量传递给 super class 是正常的。
FruitTree(int fruitSize, int height, boolean root) {
super(height, root);
}
There are about 20 parameters to the base class, and around 10 subclasses
这很可能是您的 classes 设计的问题。
没有设计模式来简化这个用例。
最佳解决方案:重构代码以使用更 Java 的方法:使用属性代替可选参数。
用例解释:广泛使用的 class 或具有大量可选参数的方法在 Java 中根本不实用,并且 kotlin 最先进的是使 java 代码更好的方法.带有 5 个可选参数的 python class,转换为没有可选参数的 Java,可能有 5 个! (5 个阶乘是 60)不同的 Java 签名……换句话说就是一团糟。
显然,任何对象都不应例行地使用庞大的参数列表进行实例化,因此通常 python classes 仅在大多数调用不需要指定的情况下演化为 classes这些可选参数,可选参数是针对异常情况的。这里的实际用例是大量可选参数的实现,其中使用超过 3 个可选参数实例化任何单个对象的情况应该非常少见。因此,具有 10 个可选参数的 class 在一个应用程序中使用了 500 次,仍然期望 3 个可选参数是在一个实例中使用过的最大值。但这只是一种在 Java 中行不通的设计方法,无论 class 被重复使用的频率如何。
在 Java 中,函数确实有可选参数,这意味着在 Java 库中以这种方式实例化对象的情况永远不会发生。
考虑一个具有一个强制实例参数和五个可能选项的对象。在 Java 中,这些选项每个都是可以由 setters 设置的属性,然后对象将被实例化,并且 setter(s) 要求设置任何相关选项,但很少需要更改该选项的默认值。
缺点是这些选项不能从构造函数中设置并保持不变,但生成的代码减少了可选参数。
另一种方法是拥有一组更少的 'swiss army knife' 个对象,用一组专门的工具代替一个全能的工具,即使代码可以被视为与相同的主题。
尽管 kotlin 中支持可选参数,但 kotlin 中的继承结构尚未针对大量使用此功能进行优化。
阅读你的问题后,我开始自己做实验,这就是我想出的:
interface TreeProperties {
val height: Int
val roots: Boolean
}
interface FruitTreeProperties: TreeProperties {
val fruitSize: Int
}
fun treeProps(height: Int = 10, roots: Boolean = true) = object : TreeProperties {
override val height = height
override val roots = roots
}
fun TreeProperties.toFruitProperty(fruitSize: Int): FruitTreeProperties = object: FruitTreeProperties, TreeProperties by this {
override val fruitSize = fruitSize
}
open class BaseTree(val props: TreeProperties)
open class FruitTree(props: FruitTreeProperties): BaseTree(props)
fun main(args: Array<String>){
val largTree = FruitTree(treeProps(height = 15).toFruitProperty(fruitSize = 5))
val rootlessTree = BaseTree(treeProps(roots = false))
}
基本上,我在接口中定义参数,并使用委托模式为子 类 扩展接口。为了方便起见,我添加了函数来生成那些也使用默认参数的接口的实例。
我认为这很好地实现了重复参数列表的目标,但也有其自身的开销。不确定是否值得。
我们有一个包,我们希望将其从 python 转换为 kotlin,以便能够使用该包迁移系统。
包中有一组 class 都是变体,或者 'flavours' 共同基础 class。
大部分代码都在基础 class 中,其中有大量可选参数。所以考虑:
open class BaseTree(val height:Int=10,val roots:Boolean=true, //......还有很多!!
class FruitTree(val fruitSize, height:Int=10, roots:Boolean=true,
// now need all possible parameters for any possible instance
):BaseTree(height=height, roots=roots //... yet another variation of same list
代码实际上不是树,我只是认为这是传达想法的一种简单方式。 base class 大约有 20 个参数,大约有 10 个 subclasses,每个 subclass 实际上需要从 base [=] 重复参数列表的相同两个变体40=]。如果参数列表发生变化,那将是一场真正的噩梦!
有Java背景的人可能会评论“20个参数太多了”,可能会忽略这是可选参数,语言特性这会影响设计的这一方面。 20 个必需参数会很疯狂,但是 10 个甚至 20 个可选参数并不少见,例如检查 sqlalchemy Table。
在 python 中,你调用一个基础 class 构造函数你可以有:
def __init__(self, special, *args, **kwargs):
super().__init(*args, **kwargs) # pass all parameters except special to base constructor
有没有人知道一种技术,使用不同的方法(可能使用接口或其他方法?)来避免为每个子class 一遍又一遍地重复此参数列表?
如果你的子类在构造函数中真的有那么多参数 -> 没办法。你需要全部通过。
但是(大多数情况下)这不是一个好兆头,constructor/function 有那么多参数...
你并不孤单。 gradle-slack 频道已经对此进行了讨论。也许在未来,我们会得到编译器的帮助,但现在,你需要自己传递参数。
您可以通过按顺序排列变量来跳过 BaseTree(height, roots)
这样的名称,但是您不能像 Python 这样的操作,因为 Python 是动态语言。
Java 也要把变量传递给 super class 是正常的。
FruitTree(int fruitSize, int height, boolean root) {
super(height, root);
}
There are about 20 parameters to the base class, and around 10 subclasses
这很可能是您的 classes 设计的问题。
没有设计模式来简化这个用例。
最佳解决方案:重构代码以使用更 Java 的方法:使用属性代替可选参数。
用例解释:广泛使用的 class 或具有大量可选参数的方法在 Java 中根本不实用,并且 kotlin 最先进的是使 java 代码更好的方法.带有 5 个可选参数的 python class,转换为没有可选参数的 Java,可能有 5 个! (5 个阶乘是 60)不同的 Java 签名……换句话说就是一团糟。
显然,任何对象都不应例行地使用庞大的参数列表进行实例化,因此通常 python classes 仅在大多数调用不需要指定的情况下演化为 classes这些可选参数,可选参数是针对异常情况的。这里的实际用例是大量可选参数的实现,其中使用超过 3 个可选参数实例化任何单个对象的情况应该非常少见。因此,具有 10 个可选参数的 class 在一个应用程序中使用了 500 次,仍然期望 3 个可选参数是在一个实例中使用过的最大值。但这只是一种在 Java 中行不通的设计方法,无论 class 被重复使用的频率如何。
在 Java 中,函数确实有可选参数,这意味着在 Java 库中以这种方式实例化对象的情况永远不会发生。
考虑一个具有一个强制实例参数和五个可能选项的对象。在 Java 中,这些选项每个都是可以由 setters 设置的属性,然后对象将被实例化,并且 setter(s) 要求设置任何相关选项,但很少需要更改该选项的默认值。
缺点是这些选项不能从构造函数中设置并保持不变,但生成的代码减少了可选参数。
另一种方法是拥有一组更少的 'swiss army knife' 个对象,用一组专门的工具代替一个全能的工具,即使代码可以被视为与相同的主题。
尽管 kotlin 中支持可选参数,但 kotlin 中的继承结构尚未针对大量使用此功能进行优化。
阅读你的问题后,我开始自己做实验,这就是我想出的:
interface TreeProperties {
val height: Int
val roots: Boolean
}
interface FruitTreeProperties: TreeProperties {
val fruitSize: Int
}
fun treeProps(height: Int = 10, roots: Boolean = true) = object : TreeProperties {
override val height = height
override val roots = roots
}
fun TreeProperties.toFruitProperty(fruitSize: Int): FruitTreeProperties = object: FruitTreeProperties, TreeProperties by this {
override val fruitSize = fruitSize
}
open class BaseTree(val props: TreeProperties)
open class FruitTree(props: FruitTreeProperties): BaseTree(props)
fun main(args: Array<String>){
val largTree = FruitTree(treeProps(height = 15).toFruitProperty(fruitSize = 5))
val rootlessTree = BaseTree(treeProps(roots = false))
}
基本上,我在接口中定义参数,并使用委托模式为子 类 扩展接口。为了方便起见,我添加了函数来生成那些也使用默认参数的接口的实例。
我认为这很好地实现了重复参数列表的目标,但也有其自身的开销。不确定是否值得。