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

a constructor for the object

       Accepted two pieces of data in that constructor and stored them as properties associated with the object instance

       Overridden a method and made it useful

       Written a main function

       Instantiated your custom object and passed in values

       Used the object to print itself out, using your overridden method

      class Person(val firstName: String, val lastName: String) { val 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") // Create another person val rose = Person("Rose", "Bushnell") println(brian) }

      You'll see a number of new things here, but none are too surprising. First, a new variable is declared inside the Person object: fullName. This is something you've already done in your main function. This time, though, because you're doing it inside the Person object, it automatically becomes part of each Person instance.

      Another small change is the addition of a new Person instance in main ; this time it's a variable named rose.

      Then, there's a new keyword: init. That bears further discussion.

      Initialize a Class with a Block

      In most programming languages, Java included, a constructor takes in values (as it does in your Person class) and perhaps does some basic logic. Kotlin does this a bit differently; it introduces the idea of an initializer block. It's this block—identified conveniently with the init keyword—where you put code that should run every time an object is created.

      In this case, the initializer block uses the new fullName variable and sets it using the first and last name properties passed in through the class constructor:

       // Set the full name when creating an instance init { fullName = "$firstName $lastName" } Then this new variable is used in toString(): override fun toString(): String { return fullName }

      WARNING As much new material as this chapter introduces, you may have just run across the most important thing that you may learn, in the long-term sense. By changing toString() to use fullName, rather than also using the firstName and lastName variables directly, you are implementing a principle called DRY: Don't Repeat Yourself.

       In this case, you're not repeating the combination of first name and last name, which was done already in the initializer block. You assign that combination to a variable, and then forever more, you should use that variable instead of what it actually references. More on this later, but take note here: this is a big deal!

      Kotlin Auto-Generates Getters and Setters

      At this point, things are going well. Part of that is all you've added, but another big help is that Kotlin is doing a lot behind the scenes. It's running code for you automatically (like that initializer block) and letting you override methods.

      It's doing something else, too: it's auto-generating some extra methods on your class. Because you made firstName and lastName property values (with that val keyword), and you defined a fullName property, Kotlin created getters and setters for all of those properties.

      Terminology Update: Getters, Setters, Mutators, Accessors

      A getter is a method that allows you to get a value. For instance, you can add this into your main function, and it will not only work, but print out just the first name of the brian Person instance:

      // Create a new person val brian = Person("Brian", "Truesby") println(brian.firstName)

      This works because you have a getter on Person for firstName. You can do the same with fullName and lastName, too. This getter is, more formally, an accessor. It provides access to a value, in this case a property of Person. And it's “free” because Kotlin creates this accessor for you.

      // Create a new person val brian = Person("Brian", "Truesby") println(brian.firstName) // Create another person val rose = Person("Rose", "Bushnell") rose.lastName = "Bushnell-Truesby"

      Just as you can get data through an accessor, you can update data through mutators.

      WARNING For the most part, I'll be calling getters accessors, and calling setters mutators. That's not as common as “getter” or “setter,” but as a good friend and editor of mine once told me, a setter is a hairy and somewhat fluffy dog; a mutator lets you update class data. The difference—and his colorful explanation—has stuck with me for 20 years.

      Now, if you've gone ahead and compiled this code, you've run into yet another odd error, and that's the last thing to fix before moving on from this initial foray into objects.

      Constants Can't Change (Sort of)

      Here's the code causing the problem:

      // Create another person val rose = Person("Rose", "Bushnell") rose.lastName = "Bushnell-Truesby"

      If you try to run this code, you'll get an error like this:

      Error: Kotlin: Val cannot be reassigned

      One of the things that is fairly unique about Kotlin is its strong stance on variables. Specifically, Kotlin allows you to not just declare the type of a variable, but also whether that variable is a mutable variable, or a constant variable.

      NOTE The terminology here is a bit confusing, so take your time. Just as with methods being declared with the fun keyword, the idea of a constant variable takes a little getting used to.

      When you declare a variable in Kotlin, you can use the keyword val, as you've already done:

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