Tobey Yeh
Tobeylearns
Published in
10 min readMay 16, 2021

--

Unit 2: Layouts

Get user input in an app: Part 1

Create a base class

介紹抽象類別建立

Class hierarchy of dwellings

In this codelab, you are going to build a Kotlin program that demonstrates how class hierarchies work, using dwellings (shelters in which people live) with floor space, stories, and residents as an example.

Below is a diagram of the class hierarchy you are going to build. At the root, you have a Dwelling that specifies properties and functionality that is true for all dwellings, similar to a blueprint. You then have classes for a square cabin (SquareCabin), round hut (RoundHut), and a round tower (RoundTower) which is a RoundHut with multiple floors.

The classes that you will implement:

  • Dwelling: a base class representing a non-specific shelter that holds information that is common to all dwellings.
  • SquareCabin: a square cabin made of wood with a square floor area.
  • RoundHut: a round hut that is made of straw with a circular floor area, and the parent of RoundTower.
  • RoundTower: a round tower made of stone with a circular floor area and multiple stories.

Create an abstract Dwelling class

Any class can be the base class of a class hierarchy or a parent of other classes.

An “abstract” class is a class that cannot be instantiated because it is not fully implemented. You can think of it as a sketch. A sketch incorporates the ideas and plans for something, but not usually enough information to build it. You use a sketch (abstract class) to create a blueprint (class) from which you build the actual object instance.

A common benefit of creating a superclass is to contain properties and functions that are common to all its subclasses. If the values of properties and implementations of functions are not known, make the class abstract. For example, Vegetables have many properties common to all vegetables, but you can't create an instance of a non-specific vegetable, because you don't know, for example, its shape or color. So Vegetable is an abstract class that leaves it up to the subclasses to determine specific details about each vegetable.

The declaration of an abstract class starts with the abstract keyword.

Dwelling is going to be an abstract class like Vegetable. It is going to contain properties and functions that are common to many types of dwellings, but the exact values of properties and details of implementation of functions are not known.

abstract class Dwelling(){
}

Add a property for building material

In this Dwelling class, you define things that are true for all dwellings, even if they may be different for different dwellings. All dwellings are made of some building material.

  1. Inside Dwelling, create a buildingMaterial variable of type String to represent the building material. Since the building material won't change, use val to make it an immutable variable.
val buildingMaterial: String

The buildingMaterial property does not have a value. In fact, you CAN'T give it a value, because a non-specific building isn't made of anything specific. So, as the error message indicates, you can prefix the declaration of buildingMaterial with the abstract keyword, to indicate that it is not going to be defined here.

  1. Add the abstract keyword onto the variable definition.
abstract val buildingMaterial: String
  1. Run your code, and while it does not do anything, it now compiles without errors.
  2. Make an instance of Dwelling in the main() function and run your code.
val dwelling = Dwelling()
  1. You’ll get an error because you cannot create an instance of the abstract Dwelling class.
Cannot create an instance of an abstract class
  1. Delete this incorrect code.

Your finished code so far:

abstract class Dwelling(){
abstract val buildingMaterial: String
}

Add a private property for number of residents

All dwellings will have a number of residents who reside in the dwelling (which may be less than or equal to the capacity), so define the residents property in the Dwelling superclass for all subclasses to inherit and use.

  1. You can make residents a parameter that is passed into the constructor of the Dwelling class. The residents property is a var, because the number of residents can change after the instance has been created.
abstract class Dwelling(private var residents: Int) {

Notice that the residents property is marked with the private keyword. Private is a visibility modifier in Kotlin meaning that the residents property is only visible to (and can be used inside) this class. It cannot be accessed from elsewhere in your program. You can mark properties or methods with the private keyword. Otherwise when no visibility modifier is specified, the properties and methods are public by default and accessible from other parts of your program. Since the number of people who live in a dwelling is usually private information (compared to information about the building material or the capacity of the building), this is a reasonable decision.

With both the capacity of the dwelling and the number of current residents defined, you can create a function hasRoom() to determine whether there is room for another resident in the dwelling. You can define and implement the hasRoom() function in the Dwelling class because the formula for calculating whether there is room is the same for all dwellings. There is room in a Dwelling if the number of residents is less than the capacity, and the function should return true or false based on this comparison.

  1. Add the hasRoom() function to the Dwelling class.
fun hasRoom(): Boolean {
return residents < capacity
}
  1. You can run this code and there should be no errors. It doesn’t do anything visible yet.

Your completed code should look like this:

abstract class Dwelling(private var residents: Int) {

abstract val buildingMaterial: String
abstract val capacity: Int

fun hasRoom(): Boolean {
return residents < capacity
}
}

練習1

Create subclasses (子類別)

Create a SquareCabin subclass

  1. Below the Dwelling class, create a class called SquareCabin.
class SquareCabin
  1. Next, you need to indicate that SquareCabin is related to Dwelling. In your code, you want to indicate that SquareCabin extends from Dwelling (or is a subclass to Dwelling) because SquareCabin will provide an implementation for the abstract parts of Dwelling.

Indicate this inheritance relationship by adding a colon (:) after the SquareCabin class name, followed by a call to initialize the parent Dwelling class. Don't forget to add parentheses after the Dwelling class name.

class SquareCabin : Dwelling()
  1. When extending from a superclass, you must pass in the required parameters expected by the superclass. Dwelling requires the number of residents as input. You could pass in a fixed number of residents like 3.
class SquareCabin : Dwelling(3)

However, you want your program to be more flexible and allow for a variable number of residents for SquareCabins. Hence make residents a parameter in the SquareCabin class definition. Do not declare residents as val, because you are reusing a property already declared in the parent class Dwelling.

class SquareCabin(residents: Int) : Dwelling(residents)

因宣告class會錯,需宣告抽象

When you declare abstract functions and variables, it is like a promise that you will give them values and implementations later. For a variable, it means that any subclass of that abstract class needs to give it a value. For a function, it means that any subclass needs to implement the function body.

In the Dwelling class, you defined an abstract variable buildingMaterial. SquareCabin is a subclass of Dwelling, so it must provide a value for buildingMaterial. Use the override keyword to indicate that this property was defined in a parent class and is about to be overridden in this class.

  1. Inside the SquareCabin class, override the buildingMaterial property and assign it the value "Wood".
  2. Do the same for the capacity, saying 6 residents can live in a SquareCabin.
class SquareCabin(residents: Int) : Dwelling(residents) {
override val buildingMaterial = "Wood"
override val capacity = 6
}

Your finished code should look like this.

abstract class Dwelling(private var residents: Int) {
abstract val buildingMaterial: String
abstract val capacity: Int

fun hasRoom(): Boolean {
return residents < capacity
}
}

class SquareCabin(residents: Int) : Dwelling(residents) {
override val buildingMaterial = "Wood"
override val capacity = 6
}

To test your code, create an instance of SquareCabin in your program.

Use SquareCabin

  1. Insert an empty main() function before the Dwelling and SquareCabin class definitions.
fun main() {

}

abstract class Dwelling(private var residents: Int) {
...
}

class SquareCabin(residents: Int) : Dwelling(residents) {
...
}
  1. Within the main() function, create an instance of SquareCabin called squareCabin with 6 residents. Add print statements for the building material, the capacity, and the hasRoom() function.
fun main() {
val squareCabin = SquareCabin(6)

println("\nSquare Cabin\n============")
println("Capacity: ${squareCabin.capacity}")
println("Material: ${squareCabin.buildingMaterial}")
println("Has room? ${squareCabin.hasRoom()}")
}

Notice that the hasRoom() function was not defined in the SquareCabin class, but it was defined in the Dwelling class. Since SquareCabin is a subclass to Dwelling class, the hasRoom() function was inherited for free. The hasRoom() function can now be called on all instances of SquareCabin, as seen in the code snippet as squareCabin.hasRoom().

  1. Run your code, and it should print the following.
Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

You created squareCabin with 6 residents, which is equal to the capacity, so hasRoom() returns false. You could experiment with initializing SquareCabin with a smaller number of residents, and when you run your program again, hasRoom() should return true.

Use with to simplify your code

In the println() statements, every time you reference a property or function of squareCabin, notice how you have to repeat squareCabin. This becomes repetitive and can be a source of errors when you copy and paste print statements.

When you are working with a specific instance of a class and need to access multiple properties and functions of that instance, you can say "do all the following operations with this instance object" using a with statement. Start with the keyword with, followed by the instance name in parentheses, followed by curly braces which contain the operations you want to perform.

Use with to simplify your code (簡化,用with代替參數)

In the println() statements, every time you reference a property or function of squareCabin, notice how you have to repeat squareCabin. This becomes repetitive and can be a source of errors when you copy and paste print statements.

When you are working with a specific instance of a class and need to access multiple properties and functions of that instance, you can say “do all the following operations with this instance object” using a with statement. Start with the keyword with, followed by the instance name in parentheses, followed by curly braces which contain the operations you want to perform.

code

fun main() {
//簡化版
val squareCabin = SquareCabin(6)

//no simplify add suarecabin add para
println(“\nSquare Cabin\n============”)
println(“Capacity: ${squareCabin.capacity}”)
println(“Material: ${squareCabin.buildingMaterial}”)
println(“Has room? ${squareCabin.hasRoom()}”)

//use with
with(squareCabin) {
println(“\nSquare Cabin\n============”)
println(“Capacity: ${capacity}”)
println(“Material: ${buildingMaterial}”)
println(“Has room? ${hasRoom()}”)
}
}
//住宅 (人數)
abstract class Dwelling(private var residents: Int) {
//建材
abstract val buildingMaterial: String
//容量
abstract val capacity: Int
//房間,當人數小於容量
fun hasRoom(): Boolean {
return residents < capacity
}
}
//木製渡材屋
class SquareCabin(residents: Int) : Dwelling(residents) {
override val buildingMaterial = “Wood”
override val capacity = 6
}

Create a RoundHut subclass

  1. In the same way as the SquareCabin, add another subclass, RoundHut, to Dwelling.
  2. Override buildingMaterial and give it a value of "Straw".
  3. Override capacity and set it to 4.
class RoundHut(residents: Int) : Dwelling(residents) {
override val buildingMaterial = "Straw"
override val capacity = 4
}
  1. In main(), create an instance of RoundHut with 3 residents.
val roundHut = RoundHut(3)
  1. Add the code below to print information about roundHut.
with(roundHut) {
println("\nRound Hut\n=========")
println("Material: ${buildingMaterial}")
println("Capacity: ${capacity}")
println("Has room? ${hasRoom()}")
}
  1. Run your code and your output for the whole program should be:
Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

Round Hut
=========
Material: Straw
Capacity: 4
Has room? true

You now have a class hierarchy that looks like this, with Dwelling as the root class and SquareCabin and RoundHut as subclasses of Dwelling.

Create a RoundTower subclass

The final class in this class hierarchy is a round tower. You can think of a round tower as a round hut made of stone, with multiple stories. So, you can make RoundTower a subclass of RoundHut.

  1. Create a RoundTower class that is a subclass of RoundHut. Add the residents parameter to the constructor of RoundTower, and then pass that parameter to the constructor of the RoundHut superclass.
  2. Override the buildingMaterial to be "Stone".
  3. Set the capacity to 4.
class RoundTower(residents: Int) : RoundHut(residents) {
override val buildingMaterial = "Stone"
override val capacity = 4
}
  1. Run this code and you get an error.
This type is final, so it cannot be inherited from

This error means that the RoundHut class cannot be subclassed (or inherited from). By default, in Kotlin, classes are final and cannot be subclassed. You are only allowed to inherit from abstract classes or classes that are marked with the open keyword. Hence you need to mark the RoundHut class with the open keyword to allow it to be inherited from.

建立不同房子給人居住

fun main() {
//簡化版
val squareCabin = SquareCabin(6)

//no simplify add suarecabin add para
println(“\nSquare Cabin\n============”)
println(“Capacity: ${squareCabin.capacity}”)
println(“Material: ${squareCabin.buildingMaterial}”)
println(“Has room? ${squareCabin.hasRoom()}”)

//use with
with(squareCabin) {
println(“\nSquare Cabin\n============”)
println(“Capacity: ${capacity}”)
println(“Material: ${buildingMaterial}”)
println(“Has room? ${hasRoom()}”)
}

val roundHut = RoundHut(3)
with(roundHut) {
println(“\nRound Hut\n=========”)
println(“Material: ${buildingMaterial}”)
println(“Capacity: ${capacity}”)
println(“Has room? ${hasRoom()}”)
}

}
//住宅 (人數)
abstract class Dwelling(private var residents: Int) {
//建材
abstract val buildingMaterial: String
//容量
abstract val capacity: Int
//房間,當人數小於容量
fun hasRoom(): Boolean {
return residents < capacity
}
}
//木製渡假屋
class SquareCabin(residents: Int) : Dwelling(residents) {
override val buildingMaterial = “Wood”
override val capacity = 6
}
//圓型屋子
class RoundHut(residents: Int) : Dwelling(residents) {
override val buildingMaterial = “Straw”
override val capacity = 4
}

https://developer.android.com/codelabs/basic-android-kotlin-training-classes-and-inheritance?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fandroid-basics-kotlin-unit-2-pathway-1%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fbasic-android-kotlin-training-classes-and-inheritance#3

--

--