2017-02-13 24 views
8

Tôi có lớp Kotlin sau đây với một nhà xây dựng chính,cú pháp xây dựng Secondary Kotlin

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

    init{ 
     println("Initializing") 
    } 

} 

Tôi muốn thêm một constructor thứ mà phân tích một fullname vào một tên firstlast và kêu gọi các nhà xây dựng chính. Tuy nhiên, tôi không thể có được đúng cú pháp ...

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

này hoạt động tốt, vì tôi không thực sự phân tích fullname trong constructor thứ yếu. Khi tôi đi trước và cố gắng phân tích fullname,

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

tôi nhận được một tài liệu tham khảo chưa được giải quyết: fullname. Nó không tồn tại trong phạm vi, nhưng nếu tôi đặt nó trong dấu ngoặc, sau đó tôi không thể gọi các nhà xây dựng chính qua this,

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

tôi nhận được một lỗi liên quan đến một thiếu invoke chức năng.

Không thể tìm thấy ví dụ điển hình về trường hợp này trên tài liệu Kotlin, xin lỗi.

+0

Bạn luôn có thể tiếp xúc với phương pháp nhà máy và ủy những người như trái ngược với nhà xây dựng, cho phép bạn quyết định khi nào thực sự ủy nhiệm, hoặc chỉ không khai báo các biến cho 'first' và 'last' trong constructor thứ hai của bạn. Nhưng nếu bạn không nhớ tôi hỏi, tại sao bạn phải phơi bày 'Người (đầu, cuối, tuổi) 'VÀ' Người (tên đầy đủ, tuổi) '? Điều gì sẽ xảy ra nếu khách hàng quên thêm dấu cách giữa đầu tiên và cuối cùng khi sử dụng 'fullName'? Bạn không thể khai báo một biến trước khi hàm tạo hàm tạo. –

+0

Đây chỉ là một ví dụ đồ chơi, tôi sẽ không thực sự xây dựng hai nhà thầu này. Bạn đang nói rằng tôi không thể sử dụng một constructor thứ cấp theo cách này? Trong java đơn giản, tôi nghĩ bạn có thể khai báo các biến trong các hàm tạo thứ cấp? Vì vậy, tôi đoán đây chỉ là một ví dụ xấu, và nó đánh vào một trường hợp sử dụng mà mã hóa tốt sẽ tránh? –

+0

Có, bạn không thể sử dụng hàm tạo thứ cấp theo cách này. 'this' là một đoàn đại biểu, đó là lý do tại sao bạn không thể sử dụng nó trong niềng răng. Bạn sẽ phải làm điều gì đó dọc theo dòng 'constructor (...): this (fullName.split (" ") [0], fullName.split (" ") [1])' có tiềm năng để lập chỉ mục giới hạn. –

Trả lời

2

giải pháp tôi sử dụng khi tôi muốn có một constructor thứ mà cần phải thực hiện một số tính toán trước khi đi qua các kết quả để các nhà xây dựng chính là một hàm trên đối tượng bạn đồng hành. Các mã để làm điều này sẽ như thế nào:

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

Sau đó bạn có thể sử dụng nó như thế này

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

Mà không phải là gọn gàng như Person("John Doe", 27) nhưng là IMO không phải là quá xấu.

+0

Ok, cảm ơn Micahel. Loại này nhắc tôi về python vì một lý do nào đó. Tôi nhớ việc tạo các hàm tạo cũng được gọi là các phương thức tĩnh. –

+0

Điều tiếp theo bạn biết, API của bạn bị ngập với các phương pháp nhà máy tĩnh thực hiện kiểm tra trường hợp góc. Điều gì sẽ xảy ra nếu bạn muốn thêm số an sinh xã hội sau này hoặc có thể thay đổi cách phân tích cú pháp tên? Có thể bị buộc phải vi phạm một số nguyên tắc khá hữu ích, chẳng hạn như Open/Close –

+0

@VinceEmigh Thường thì hàm tạo/hàm nhà máy là _exactly_ nơi bạn muốn kiểm tra trường hợp góc, tốt hơn nhiều so với việc quét các kiểm tra đó ở hàng chục địa điểm trong suốt mã của bạn. Nhưng, như mọi khi, nó là vấn đề cân bằng. Tôi sẽ bênh vực chống lại việc tạo ra các chức năng của nhà máy một cách mù quáng cho mọi trường hợp - nhưng nếu đó là một mô hình trồng hoa nhiều lần - thì một trường hợp chắc chắn có thể được tạo ra cho nó. –

1

Cuộc gọi xây dựng qua this phải là cuộc gọi đầu tiên. Đây là lý do tại sao nó được xử lý như một đại biểu, chứ không phải là một lời gọi phương thức bình thường. Điều này có nghĩa là bạn không thể khai báo biến trước khi cuộc gọi được ủy nhiệm.

Bạn có thể giải quyết điều này bằng cách đơn giản là nội tuyến giá trị nào bạn lên kế hoạch về lưu trữ trong các biến:

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

Nhưng điều này chỉ có thể có khả năng nằm ngoài giới hạn nếu một tên cuối cùng đã không được chỉ định, hoặc nếu khách hàng quyết định sử dụng - hoặc một số ký tự khác làm dấu phân tách. Trên hết, đó là một mắt đau.

Phân tích Thiết kế

Vấn đề với cấu trúc của bạn là cho lớp Person trách nhiệm xác định tên đầu tiên và cuối cùng. Điều này làm giảm khả năng sử dụng lại của lớp đó, vì nó sẽ bị giới hạn ở một dạng phân tích cú pháp. Đây là lý do tại sao việc phân tích tên không nên được thực hiện bởi Person.

Thay vào đó, bạn nên trưng ra công cụ xây dựng chính của mình, sau đó yêu cầu khách hàng của Person tách họ và tên.

Ví dụ giải pháp

Hãy tưởng tượng chúng tôi đang đọc tên từ tệp. Mỗi dòng trong tệp bao gồm tên đầy đủ.

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

Đây là sự sang trọng bạn đang cố gắng cung cấp cho khách hàng của mình: cho phép họ chỉ cần nhập tên mà không phải lo lắng về việc phân tích cú pháp.

Vấn đề với điều này là sự thiếu an toàn: nếu dòng chỉ chứa tên đầu tiên thì sao? Điều gì xảy ra nếu tệp không sử dụng khoảng trắng để tách họ và tên? Bạn sẽ bị buộc phải xác định các loại Person mới chỉ để xử lý các kết hợp tên/họ khác nhau.

Thay vào đó, phân tích nên xảy ra bên ngoài của lớp:

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

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

Bây giờ trách nhiệm đã được đưa ra khỏi Person, chúng tôi có thể cung cấp cho các trách nhiệm cho một đối tượng mới nếu chúng ta muốn:

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

    personList.add(Person(firsrName, lastName)) 
}) 
Các vấn đề liên quan