二级构造语法kotlin

时间:2017-02-13 16:02:02

标签: kotlin

我有以下带有主要构造函数的kotlin类,

class Person(first: String, last: String, age: Int){ 

    init{
        println("Initializing")
    }

}

我想添加一个辅助构造函数,它将全名解析为firstlast名称并调用主构造函数。但是,我无法正确理解语法......

class Person(first: String, last: String, age: Int){  

    // Secondary constructor
    constructor(fullname: String, age: Int):
        this("first", "last", age)
        {
            println("In secondary constructor")
        }

    init{
        println("Initializing")
    }
}

这很好用,因为我实际上并没有在辅助构造函数中解析fullname。当我继续尝试解析全名时,

constructor(fullname: String, age: Int):
var first = fullname.split()[0];
...
{
    println("In secondary constructor")
}

我得到一个未解决的引用:fullname。它并不存在于范围内,但是如果我把它放在括号中,那么我就不能通过this调用主构造函数,

constructor(fullname: String, age: Int):
{
    var first = fullname
    this(first, "foo", age)
    println("In secondary constructor")
}

我收到一条涉及缺少invoke功能的错误。

在Kotlin文档上找不到这个案例的好例子,抱歉。

2 个答案:

答案 0 :(得分:2)

当我希望在将结果传递给主构造函数之前需要执行某些计算的辅助构造函数时,我使用的解决方案是伴随对象上的函数。执行此操作的代码如下所示:

class Person(first: String, last: String, age: Int) {  

    companion object {
        fun fromFullNameAndAge(fullname: String, age: Int) : Person {
          println("In secondary constructor")
          var bits = fullname.split()
          // Additional error checking can (and should) go in here.
          return Person(bits[0],bits[1],age)
        }
    }

    init{
        println("Initializing")
    }
}

然后你可以像这样使用它

var p = Person.fromFullNameAndAge("John Doe", 27)

这不像Person("John Doe", 27)那样整洁,但IMO也不算太差。

答案 1 :(得分:1)

通过this进行的构造函数调用必须是第一次调用。这就是它作为委托处理的原因,而不是普通的方法调用。这意味着您无法在委托调用之前声明变量。

您可以通过简单地内联您计划存储在变量中的任何值来解决此问题:

constructor(fullName : String, age : int) : this(fullName.split(" ")[0], fullName.split(" ")[1])

但如果未指定姓氏,或者客户端决定使用-或其他字符作为分隔符,则可能会超出范围索引。最重要的是,这是一个眼睛疼痛。

设计分析

您的结构问题是让Person类负责确定名字和姓氏。这会降低该类的可重用性,因为它将限于一种解析形式。这就是Person不能执行名称解析的原因。

相反,您应该公开您的主要构造函数,然后让Person的客户端分隔名字和姓氏。

解决方案示例

想象一下,我们正在从文件中读取名称。文件中的每一行都包含一个全名。

nameFile.forEachLine({ personList.add(Person(it)) })

这是您试图为客户提供的奢侈品:允许他们只输入一个名称,而无需担心解析它。

问题在于缺乏安全性:如果该行只包含名字怎么办?如果文件没有使用空格来分隔名字和姓氏怎么办?您将被迫定义新的Person类型,以处理不同的名字/姓氏组合。

相反,解析应该在类之外进行:

file.forEachLine({
    val firstName = ...
    val secondName = ...

    personList.add(Person(firstName, secondName))
})

既然责任已经从Person中解脱出来,我们可以根据需要对新对象负责:

val parser = NameParser(" ") //specify delimiter
file.forEachLine({
    val firstName = parser.extractFirstName(it)
    val lastName = parser.extractLastName(it)

    personList.add(Person(firsrName, lastName))
})