我是一名Java资深人士,正在学习Kotlin。我对如何将构造函数参数提供给子类并将其转换为父类所需的参数感到有些困惑。当提供给孩子的参数不适合父母时会出现问题。
例如,当我有IntelliJ IDEA时,将以下内容转换为Kotlin ...
class Base
{
final int w;
final int h;
Base(int w, int h)
{
this.w = w;
this.h = h;
}
}
class Derived extends Base
{
Derived(int x)
{
// some complex derivation
Converter c = new Converter(x);
super(c.a, c.b);
}
}
class Converter
{
final int a;
final int b;
Converter(int x)
{
a = x + 2;
b = x - 2;
}
}
我得到以下信息,包括指示位置的错误,表明没有为w
和h
传递参数...
open class Base(val w: Int, val h: Int)
class Derived(x: Int) : Base() {
// ^ error
init {
// some complex derivation
val c = Converter(x)
// How do I provide c.a and c.b?
}
}
class Converter(x: Int) {
val a: Int
val b: Int
init {
a = x + 2
b = x - 2
}
}
该问题的一般解决方案是什么? (很明显,我没有做下面所示的任何简单的事情。我只是为了说明问题而简化了。)
答案 0 :(得分:1)
我建议使用更简单的私有构造函数,并添加工厂方法进行转换,例如:
class Derived private constructor(val w: Int, val h: Int) : Base(w, h) {
companion object {
operator fun invoke(x: Int): Derived {
// Some complex derivation…
val c = Converter(x)
return Derived(c.a, c.b)
}
}
}
可以用与构造函数完全相同的方式调用它,例如val d = Derived(1)
,但它有几个优点:
在调用构造函数之前,它可以做很多处理。
在适当的情况下,它可以返回缓存的值而不是新实例。
它可以返回一个子类。 (因此Derived
可以是抽象类,或者甚至根本不需要它。)确切的类在调用之间可能有所不同,甚至可以是匿名类型。
它可以有一个名字。如果您需要采用相同参数的多种方法,则这一点尤其重要。 (例如,可以由矩形或极坐标构造的Point对象。)但是,factory方法不需要特定的名称;如果您在同伴对象中实现invoke()
方法(如上所述),则可以使用与构造函数完全相同的方法来调用它。
它使更改类的实现变得更容易,而又不影响其公共接口。
尽管有一个缺点:
与基于属性的答案不同,这不需要对类的其余部分进行任何更改,也不需要保留Convertor
对象;纯粹是调用构造函数的另一种方式。
(在某些情况下,您可能不需要将主构造函数设为私有;只要签名不匹配,它就可以提供工厂方法的替代方法。)
答案 1 :(得分:0)
上面JB Nizet的评论使我走上了正确的道路:您也无法在Java中以这种方式进行操作。我一直以尝试以Kotlin方式做事而被打结。在这种情况下,我将尝试在Java中将使用访问器方法的所有地方使用属性。
一种解决方案是使基本属性抽象化:
abstract class Base {
abstract val w: Int
abstract val h: Int
// base implementation
}
class Derived(x: Int) : Base() {
private val c = Converter(x)
override val w: Int
get() = c.a
override val h: Int
get() = c.b
}
class Converter(x: Int) {
// implementation not relevant here
val a = x + 2
val b = x - 2
}