2017-02-13 82 views
8

我有一個主構造以下科特林類,二級構造語法科特林

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") 
} 

我得到一個未解決的參考:全名。它並不在範圍中不存在,但如果我把它放在括號,則無法通過this調用主構造,

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

我收到涉及失蹤invoke功能的錯誤。

在Kotlin文檔中找不到這種情況的一個好例子,對不起。

+0

您可以隨時暴露工廠方法,並將它們委託給構造函數,讓您決定何時實際委派,或者只是不在第二個構造函數中爲'first'和'last'聲明變量。但是,如果你不介意我問,爲什麼你必須揭露'人(第一,最後,年齡)'和'人(全名,年齡)'?如果客戶忘記在使用'fullName'時在第一個和最後一個之間添加空格會怎麼樣?您無法在構造函數委派之前聲明變量。 –

+0

這只是一個玩具的例子,我實際上不會構建這兩個構造函數。你是說我不能用這種方式使用二級構造函數嗎? 在普通的java中,我認爲你可以在二級構造函數中聲明變量?所以我想這僅僅是一個不好的例子,而且它打擊了一個良好編碼會避免的用例? –

+0

是的,你不能以這種方式使用二級構造函數。 '這是'代表團,這就是爲什麼你不能在大括號內使用它。你必須按照'constructor(...):this(fullName.split(「」)[0],fullName.split(「」)[1])'的方式做一些事情,這有可能被索引出來的界限。 –

回答

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不是太糟糕。

+0

好的,謝謝Micahel。這種想法讓我想起了python。我記得構造函數也被稱爲靜態方法。 –

+0

接下來你知道,你的API充斥着執行角落情況檢查的靜態工廠方法。如果您稍後想要添加社會安全號碼,或者可能更改姓名的解析方式,該怎麼辦?將被迫違反一些非常有用的原則,例如打開/關閉 –

+0

@VinceEmigh通常構造函數/工廠函數是_exactly_您希望進行角落案例檢查的地方,遠遠好於在整個代碼中打亂十幾個地方的那些檢查。但是,一如既往,這是一個平衡的問題。我會主張不要爲每種情況盲目地創建工廠功能 - 但如果它是一種多次出現的模式 - 那麼肯定可以爲它做出一個案例。 –

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)) 
})