2017-02-13 3 views
8

У меня есть следующий класс Котлин с первичным конструктором,Вторичный синтаксис строительства Котлин

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

    init{ 
     println("Initializing") 
    } 

} 

Я хотел бы добавить дополнительный конструктор, который разбирает FULLNAME в first и last имя и вызывает первичный конструктор. Тем не менее, я не могу получить правильный синтаксис ...

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

Да, вы не можете использовать вторичный конструктор таким образом. 'this' является делегацией, поэтому вы не можете использовать ее в фигурных скобках. Вам нужно что-то сделать по строкам '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

Хорошо, спасибо Микель. Это почему-то напоминает мне python. Я помню создание конструкторов, которые также называются статическими методами. –

+0

Следующее, что вы знаете, ваш API наводнен статическими заводскими методами, которые выполняют проверку в конце концов. Что, если вы захотите добавить номер социального страхования позже или, возможно, измените, как анализируются имена? Было бы вынуждено нарушить некоторые довольно полезные принципы, такие как Open/Close –

+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)) 
}) 
Смежные вопросы