Kotlinlearncs.online LogoJava

    ← Prev

    Index

    Next →

    Kotlin
    Java
    • Polymorphism : 31

    • Inheritance : 30

    • Data Modeling 1 : 29

    • Companion Objects : 28

    • Encapsulation : 27

    • Constructors : 26

    • Objects, Continued : 25

    • Introduction to Objects : 24

    • Compilation and Immutability : 23

    • Practice with Collections : 22

    • Maps and Sets : 21

    • Lists and Type Parameters : 20

    • Imports and Libraries : 19

    • Multidimensional Arrays : 18

    • Practice with Strings : 17

    • null : 16

    • Algorithms and Strings : 15

    • Strings : 14

    • Functions and Algorithms : 13

    • Practice with Functions : 12

    • More About Functions : 11

    • Errors and Debugging : 10

    • Functions : 9

    • Practice with Loops and Algorithms : 8

    • Algorithms I : 7

    • Loops : 6

    • Arrays : 5

    • Compound Conditionals : 4

    • Conditional Expressions and Statements : 3

    • Operations on Variables : 2

    • Variables and Types : 1

    • Hello, world! : 0

    Inheritance

    open class Pet(val name: String)
    class Dog(name: String, val breed: String) : Pet(name) {
    override fun toString(): String {
    return "A dog named $name is a $breed"
    }
    }
    val chuchu = Dog("Chuchu", "mutt")
    println(chuchu)

    Next we begin our exploration of relationships between Kotlin classes. So far the classes that we’ve created have stood alone—or at least we thought. In this lesson we’ll see that all Kotlin classes are related to each other, and how to utilize those connections to reflect real-world relationships and improve our code.

    First, A Puzzle
    First, A Puzzle

    Let’s begin the lesson with a puzzle:

    class Pet
    val chuchu = Pet()
    println(chuchu.toString())

    If we compile and run this code, we see something printed. Which is… weird! Right? The snippet above uses dot notation to call a method on chuchu called toString. But where is that instance method defined? Do you see it? I don’t! So why does this code work? On some level, this lesson is about figuring that out.

    Inheritance
    Inheritance

    Kotlin allows us to establish relationships between classes. Specifically, Kotlin allows one class to inherit state and behavior by extending another class. Let’s look at an example of this:

    // I'm not using encapsulation here for the sake of brevity...
    open class Person(var name: String)
    // Student extends Person and so inherits its state and behavior
    class Student(name: String, var level: String) : Person(name)
    val you = Student("You", "Freshman")
    // This already makes sense to us: Student has an String instance variable named level
    // It's declared in the declaration of Student
    you.level = "Freshman"
    // But where is this instance variable coming from?
    // It's inherited from Person!
    you.name = "Excellent Student"
    println("${you.name} is a ${you.level}")

    Inheritance Terminology and Syntax
    Inheritance Terminology and Syntax

    We use the : notation to create a relationship between two classes. In the example above, we say that Student extends Person.

    Note also that the parent must be marked as open. This allows it to be extended. If we omit this keyword, attempts to extend the class will fail:

    class Person
    class Student : Person

    This relationship is one way. The terminology that we use here is helpful. We refer to the class that is extended as the parent and the class that extends as the child:

    // Person is the parent of Student
    open class Person
    // Student is the child of Person
    class Student : Person()

    This helps us remember that we cannot create circular class relationships. This won’t compile:

    open class One : Two
    open class Two : One

    We can also establish multiple levels of inheritance. When we do, we use similar family-based terminology:

    // Person is the parent of Student and the ancestor of Freshman
    open class Person
    // Student is the child of Person
    open class Student : Person()
    // Freshman is the child of Student and a descendant of Person
    class Freshman : Student()
    // Sophmore is the child of Student and a descendant of Person
    class Sophomore : Student()

    Parent Class Initialization
    Parent Class Initialization

    When we extend a class, we need to make sure that our parent class is set up properly when instances of our class are created. For example, consider the hierarchy below. Whenever an instance of Student is created, we are also creating an instance of Person. So we need to make sure that the Person constructor gets called. Kotlin forces us to do this correctly. Let’s see how:

    open class Person(val name: String)
    class Student : Person()
    val student = Student()

    private
    private

    As a final observation, note that private still works the way that we expect. A class that extends another does not gain access to its private variables:

    open class Person(private val name: String)
    class Student(name: String) : Person(name) {
    fun greet(): String {
    return "Hello, $name"
    }
    }

    Any
    Any

    However, none of this really resolves our puzzle. We still don’t know why this works:

    class Pet
    val chuchu = Pet()
    println(chuchu.toString())

    Pet doesn’t extend anything. So where is toString coming from?

    To fill in the missing piece of the puzzle, we need to meet the class that sits at the top of Kotlin’s class hierarchy, Any:

    Solve: Simple Person Inheritance (Practice)

    Created By: Geoffrey Challen
    / Version: 2020.9.0

    Create a class called Student that inherits from a class called Person. (Do not create Person. It is already available.) Define a single Student constructor that takes a String? value (name) and an Int value (university ID number), in that order. You should call the Person constructor and pass the String argument. (You don't need to do anything else with it.) Provide a publicly-readable but not writable property ID storing the university ID number. Reject negative ID numbers using require.

    Overriding Inherited Methods
    Overriding Inherited Methods

    So it’s nice and all that every class will inherit a toString method from Any. But this method really isn’t very useful!

    class Pet(val name: String)
    val chuchu = Pet("Chuchu")
    println(chuchu.toString())

    For example, given that my Pet has a String name, I might want to display that instead. Can we do this? Yes! Let’s look at how:

    class Pet(val name: String)
    val chuchu = Pet("Chuchu")
    println(chuchu.toString())

    We’ll get into this more tomorrow and review exactly how Kotlin locates various method and field names when it compiles your code.

    Solve: Score Tracker

    Created By: Geoffrey Challen
    / Version: 2021.9.0

    Create a class named ScoreTracker that we'll use to keep track of the scores of two players who are playing a game. Players playing this game always take turns, and Player 1 always plays first. Both players scores start at zero.

    Each ScoreTracker should provide a method score that accepts a number of points and does not return a value. If it is Player 1's turn, the points (which might be negative) get added to their score. Otherwise they get added to Player 2's score.

    You should also provide a method currentlyAhead that returns 1 if Player 1 is ahead, 2 if Player 2 is ahead, and 0 if it's a tie.

    You should not expose any of your state publicly, and ScoreTracker does not need to provide a public constructor.

    When your class is complete, here is how it should work:

    CS People: Safiya Noble
    CS People: Safiya Noble

    If you’re like me, you use a search engine constantly. But have you ever stopped to consider where the results come from? The Internet is huge! Without search, most of us would never find our way around. And so what ends up on the first page of results matters. A lot.

    Professor and MacArthur Award Winne Safiya Noble has examined how biases infiltrate Google and other search engine results in her seminal book “Algorithms of Oppression”. Listen to her describe some of her work and its broader implications:

    More Practice

    Need more practice? Head over to the practice page.