Programming Kotlin Applications. Бретт Мак-Лахлин

Читать онлайн.
Название Programming Kotlin Applications
Автор произведения Бретт Мак-Лахлин
Жанр Программы
Серия
Издательство Программы
Год выпуска 0
isbn 9781119696216



Скачать книгу

_lastName: String, _height: Double, _age: Int, _hasPartner: Boolean) { lateinit var fullName: String

      Remove the assignment to “” and add lateinit. Now, your class will compile again! No problem.

      WARNING lateinit has some pretty specific limitations. It only works with var, not val, and the properties can't be declared within the constructor itself. You also can't create a custom accessor or mutator for a lateinit-declared property. So it's not a fix-all. In fact, it's not even a fix in this case, really.

      So this is still a fragile solution, and in some cases, even more dangerous than just using a dummy value to start with.

      Assign Your Property the Return Value from a Function

      It's worth taking a minute to remember what the actual problem here is. You need to ensure that Kotlin sees an assignment of a value to fullName in one of three legal places:

       In the class's primary constructor

       In the property's declaration

       In the class's init block

      You've also seen that simply calling another function—like updateName()—and letting that function do the assignment won't cut it.

      The first thing we need to do is change our function. Why? Because it currently doesn't return a value—and that means that you can't use the function as the right side of an assignment. In other words, you can't get the all-important:

      var fullName: String = // something here... like a function!

      So change updateName() to actually return a value. While you're at it, let's also change the name to better reflect what it does now:

      fun combinedNames(): String { return "$firstName $lastName" }

      Now, you need a little cleanup:

      1 Remove the reference to updateName() from init.

      2 Assign fullName the return value from this function.

      3 While you're at it, remove the init block entirely.

      4 Move the declaration of the fullName property from the first line in Person to after all the other properties are declared.

      WARNING Don't miss that last item! Now that you're using the values of firstName and lastName to initialize fullName, the order of property declaration matters. You need firstName and lastName to get assigned the values from the primary constructor before fullName is declared and combinedNames() is run.

      package org.wiley.kotlin.person class Person(_firstName: String, _lastName: String, _height: Double, _age: Int, _hasPartner: Boolean) { var firstName: String = _firstName set(value) { field = value } var lastName: String = _lastName set(value) { field = value } var height: Double = _height var age: Int = _age var hasPartner: Boolean = _hasPartner var fullName: String = combinedNames() fun combinedNames(): String { return "$firstName $lastName" } override fun toString(): String { return fullName } }

      Sometimes You Don't Need a Property!

      Unbelievably, after all this work, the output from your code is still incorrect if a last name is changed after object instance creation. One simple fix here is to update the two custom mutators, for firstName and lastName, to update fullName, like this:

      var firstName: String = _firstName set(value) { field = value // Update the full name fullName = combinedNames() } var lastName: String = _lastName set(value) { field = value // Update the full name fullName = combinedNames() }

      Brian Truesby Rose Bushnell Rose Bushnell-Truesby

      But before you pop the champagne, take a minute to think this through. Is this really a good solution? You have several problems here, all of which are nontrivial. See if these make sense; by now, they all should:

       You've got the same piece of code—the assignment to fullName—in both the firstName and lastName mutator. In general, anytime you see the same code twice, that's not great. Try to avoid repeating code, as it becomes error-prone.

       You're going to a lot of work to set the value of a property that has no special behavior. It's just the combination of the first and last names of the instance!

       You already have a function that gives you the full name; it's called combinedNames().

      A much better approach, believe it or not, is to just remove fullName altogether. In fact, there's a lot that can be removed here.

      Let's use combinedNames() to handle the combination of firstName and lastName. Then you can remove the declaration of the fullName property altogether. But if you do that, then you can actually remove the custom mutator for firstName and lastName, too!

      package org.wiley.kotlin.person class Person(_firstName: String, _lastName: String, _height: Double, _age: Int, _hasPartner: Boolean) { var firstName: String = _firstName var lastName: String = _lastName var height: Double = _height var age: Int = _age var hasPartner: Boolean = _hasPartner fun fullName(): String { return "$firstName $lastName" } override fun toString(): String { return fullName() } }

      The big takeaway here is that when a language seeks to really enforce type safety—as Kotlin does—then nearly everything in the language gets affected. Properties are required to have values, and have them early, so that Kotlin can ensure the correct types are used. The mutability