Programming Kotlin Applications. Бретт Мак-Лахлин
val brian = Person("Brian", "Truesby")
But you can also use the keyword var
, something you haven't done yet. That would look like this:
var brian = Person("Brian", "Truesby")
First, in both cases, you end up with a variable; val
does not stand for value, for example, but is simply another way to declare a variable, alongside var
. When you use val
, you are creating a constant variable. In Kotlin, a constant variable can be assigned a value once, and only once. That variable is then constant and can never be changed.
You created the lastName
variable in Person
with this line:
class Person(val firstName: String, val lastName: String) {
That defines lastName
(and firstName
) as a constant variable. Once it's passed in and assigned when the Person
instance is created, it can't be changed. That makes this statement illegal:
rose.lastName = "Bushnell-Truesby"
To clear up the odd error from earlier, what you need instead is for lastName
to be a mutable variable; you need it to be changeable after initial assignment.
NOTE Not to beat a hairy and somewhat fluffy dog to death, but here is another reason to use mutator over setter; a mutator allows you to mutate a mutable variable. This aligns the terminology much more cleanly than using “setter.”
So change your Person
constructor to use var
instead of val
. This indicates that firstName
and lastName
can be changed:
class Person(var firstName: String, var lastName: String) {
Now you should be able to compile the program again, without error. In fact, once you've done that, make a few other tweaks. You want to end up with your code looking like Listing 1.7.
LISTING 1.7 Using mutable variables
class Person(var firstName: String, var lastName: String) { var fullName: String // Set the full name when creating an instance init { fullName = "$firstName $lastName" } override fun toString(): String { return fullName } } fun main() { // Create a new person val brian = Person("Brian", "Truesby") println(brian) // Create another person val rose = Person("Rose", "Bushnell") println(rose) // Change Rose's last name rose.lastName = "Bushnell-Truesby" println(rose) }
Here, fullName
has been made mutable, and there's a little more printing in main
. It should compile and run without error now.
But wait! Did you see your output? There's a problem! Here's what you probably get when you run your code:
Brian Truesby Rose Bushnell Rose Bushnell
Despite what Meat Loaf had to say about two out of three not being bad, this is not great. Why is Rose's name in the last instance not printing with her new last name?
Well, to solve that, it's going to take another chapter, and looking a lot more closely at how Kotlin handles data, types, and more of that automatically running code.
2 It's Hard to Break Kotlin
WHAT'S IN THIS CHAPTER?
Dive deeper into Kotlin types and variables
More on mutability
Creating custom accessors and mutators
Assignment, during and after variable creation
Type safety and what it means
UPGRADE YOUR KOTLIN CLASS GAME
You've already had a solid introduction to Kotlin, including building your first object. So we're going to get right to work. There's that nagging issue with a person's last name showing incorrectly, but before fixing that, you're ready to up how you build classes from a pre-compile point of view.
So far, you've used a single file to contain both your class ( Person
) and your main
function. That's not scalable, though. Before long, you're going to have lots of classes—and that's classes, not instances of classes.
NOTE As a quick refresher, a class is a definition. You have a Person
class, and you might also have a Car
class or a House
class. An instance of a class is a specific piece of memory that holds a representation of an object. In the last chapter, when you created a brian
or a rose
variable, you assigned those instances of the Person
class.
It's pretty easy to get lots of instances of a class, and then to also have lots of different classes defined as well. In those cases, things get messy if you're just using a single file.
If you throw all your classes into one file, that file is going to be a mess. It's better to separate each of your classes into their own files: one file per class (in general), and then if you have a main
function, give that its own file, too.
Name a File According to Its Class
Go ahead and create a new Kotlin project. You can call it whatever you like; Kotlin_ch02
works if you need a suggestion. Then, in the src/
folder, create a new file called Person
. Your IDE will likely add the .kt
extension for you. Then, just drop in your existing Person
code from Chapter 1. That code is shown again in Listing 2.1 for good measure.
LISTING 2.1: The Person code from Chapter 1
class Person(var firstName: String, var lastName: String) { var fullName: String // Set the full name when creating an instance init { fullName = "$firstName $lastName" } override fun toString(): String { return fullName } }
Now create another file, also in src/
, and call it PersonApp
. You can then drop your existing main
function into that. That bit of code is reprised in Listing 2.2.
LISTING 2.2: Main function for testing the Person class
fun main() { // Create a new person val brian = Person("Brian", "Truesby") println(brian) // Create another person val rose = Person("Rose", "Bushnell") println(rose) // Change Rose's last name rose.lastName = "Bushnell-Truesby" println(rose) }
At this point, you should be able to run PersonApp
just as you did in Chapter