[Classes and Inheritance in Kotlin]

Previous: [Add pictures to the Dice Roller app]

1. Preparation

prerequisite

  • Get familiar with how to modify Kotlin programs using Kotlin Playground .
  • Get an introduction to the basic concepts of Kotlin programming introduced in Unit 1 of this course (previous articles), especially main()procedures , functions with parameters that return values, variables, data types and operations, and if/elsestatements.
  • Ability to define classes in Kotlin, create object instances from that class, and access its properties and methods.

Learning Content

  • Create a Kotlin program that utilizes inheritance to implement class hierarchies.
  • Extend a class, replace its existing functions, and add new functions.
  • Choose the correct visibility modifier for the variable.

build content

  • A Kotlin program containing different types of housing implemented as a class hierarchy.

required conditions

Second, what is the class hierarchy?

It's natural for people to divide things with similar properties and behavior into groups, and even to create some type of hierarchy between these groups. For example, you can have a broad category such as Vegetables, and within that category you can have more specific types such as Beans. Within legumes, you can break down more specific types such as peas, soybeans, lentils, chickpeas, and soybeans.

This can be represented as a hierarchy, since beans contain or inherit all the properties of vegetables (for example, they belong to plants and are edible). Likewise, peas, soybeans, and lentils all share the properties of legumes, but also have their own unique properties.

Let's see how to express this relationship in programming terms. If you will Vegetablebe a class in Kotlin, you can create a child or subclass of Legumethe As Vegetableclass . This means that Vegetableall properties and methods of the class are inherited by Legumethe class (ie, these properties and methods are also available for the latter).

You can represent this relationship in a class hierarchy diagram, as shown below. You can Vegetablerefer to (vegetables) as Legumethe parent or superclass of (beans) class.

insert image description here

You can continue to extend the class hierarchy by Legumecreating subclasses of such as Lentil(lentils) and Chickpea(garbanzopeas). This would make Legumeis both Vegetablea child or subclass of Lentiland Chickpeaa parent or superclass of and . VegetableIs the root or top-level class (also known as the base class) of this hierarchy.

insert image description here

Note: Terminology Summary
The following summarizes the terms used in this article and what they mean in the context of Kotlin classes.

  • class hierarchy . An arrangement that organizes classes into a parent and child hierarchy. Hierarchy diagrams are usually drawn with parents on top and children on the bottom.
  • child or subclass . Any class that is below another class in the hierarchy.
  • parent, parent class, or base class . Any class with one or more subclasses.
  • Root class or top-level class . A class at the top (or root) of a class hierarchy.
  • inherit . A case where a subclass contains (or inherits) all the properties and methods of its parent class. With inheritance, you can share and reuse code, making programs easier to understand and maintain.

Inheritance in Android classes

As you've done in previous articles, while you can write Kotlin code without classes, many parts of Android are provided to you in the form of classes, including activities, views, and view groups. Therefore, understanding the class hierarchy is fundamental to Android application development and will help you take advantage of the many capabilities provided by the Android framework.

For example, Android has a Viewclass that represents a rectangular area on the screen and is responsible for drawing and event handling. TextViewThe class is a subclass of Viewthe class , which means that TextViewinherits all the properties and functions of Viewthe class , and adds specific logic for displaying text to the user.

insert image description here

One level down, EditTextand Buttonthe class is TextViewa child of the class. They TextViewinherit Viewall the properties and methods of the and classes, and add their own specific logic. For example, EditTextadded its own functionality to be able to modify text on the screen.

Instead of copying all the logic from the and classes and pasting it into the class , Viewyou can directly subclass the class , which in turn subclasses the class. The code in the class can then focus on other aspects of making this UI component different from other views.TextViewEditTextEditTextTextViewViewEditText

At the top of the documentation page on the Android classes on the developer.android.com website , you can see a diagram of the class hierarchy. If you see it at the top of the hierarchy kotlin.Any, it's because in Kotlin all classes have a common parent Any. Please click here for details.

insert image description here

As you can see, learning to take advantage of inheritance relationships between classes can make your code easier to write, reuse, read, and test.

3. Create a base class

Housing Class Hierarchy

In this article, you'll build a Kotlin program that demonstrates how class hierarchies work using the example of a house (where people live) with building spaces, floors, and occupants.

Below is a diagram of the class hierarchy you'll build. At the root, there is a dwelling ( Dwelling), which specifies properties and functions that apply to all dwellings, similar to a blueprint. Then there are classes for square cabins ( SquareCabin), round cabins ( RoundHut), and round towers ( RoundTower) (that is, multi-story ones RoundHut).

insert image description here

The class you will implement:

  • Dwelling: A base class representing a non-specific housing that stores information common to all housing.
  • SquareCabin: A square log cabin built of wood with a square building plane.
  • RoundHut: A circular hut made of straw, with a circular building plane, which is the parent RoundTowerof .
  • RoundTower: A multi-storey circular tower built of stone, with a circular building plan.

Create Dwelling abstract class

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

An "abstract" class is one that cannot be instantiated because it is not fully implemented. You can think of it as a sketch. Sketches contain ideas and plans for something, but often not enough information to complete a build. You can use a sketch (abstract class) to create a blueprint (class) from which to build actual object instances.

A common benefit of creating a parent class is the ability to contain properties and functions that are common to all its subclasses. If the value of the attribute and the implementation of the function are unknown, it can be used as an abstract class. For example, Vegetablesthere are many properties that are common to all vegetables, but you can't create an instance of a non-specific vegetable because you don't know something about it, such as its shape or color. Therefore, Vegetableit is an abstract class, and the specific details of each vegetable are determined by subclasses.

The declaration of an abstract class begins with abstractthe keyword .

Note: Later in this course, you'll encounter some of the abstract classes in Android that require subclassing.

DwellingVegetableLike , will also be an abstract class. It will contain many properties and functions that are common to classes of housing, but the exact values ​​of the properties and implementation details of the functions are unknown.

  1. Visit the Kotlin Playground at: https://developer.android.com/training/kotlinplayground .

  2. In the editor, delete the in main()the function println("Hello, world!").

  3. Then, add the following code below main()the function to create a classDwelling named .abstract

abstract class Dwelling(){
    
    
}

Add properties about building materials

In this Dwellingclass , you can define attributes that apply to all housing even though their values ​​may vary from housing to housing. All housing is constructed from certain building materials.

  1. DwellingIn , create a variableString of type to represent the building material. buildingMaterialSince the building material doesn't change, you can use valto make it immutable.
val buildingMaterial: String
  1. Running the program, the system displays the following error message.
Property must be initialized or be abstract

buildingMaterialProperty has no value. You can't actually provide a value for it, because a neutral building isn't built from any specific building material. Therefore, as the error message indicates, you can abstractprefix buildingMaterialthe declaration with the keyword to indicate that the property will not be defined there.

  1. Add abstractthe keyword to the variable definition.
abstract val buildingMaterial: String
  1. Run your code, although it doesn't do anything, it now compiles without any errors.
  2. Build an instance in main()the function Dwellingand run the code.
val dwelling = Dwelling()
  1. You will get an error message because you cannot create an instance of the Dwelling abstract class.
Cannot create an instance of an abstract class
  1. Remove this error code.

Your completed code so far looks like this:

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

Add properties about capacity.

Another attribute of housing is capacity, or how many people can live in the house.

There will be no change to the occupancy of any housing. However, the capacity cannot be set in Dwellingthe parent class, but should be defined in the subclass for the particular type of housing.

  1. DwellingIn , add an integercapacity named .abstractval
abstract val capacity: Int

Add a private property about the number of guests

All houses have a certain number residents(that is, the number of people living in the house, which may be less than or equal to capacity), Dwellingso residentsthe property should be defined in the parent class so that all subclasses can inherit and use it.

  1. You can pass residentsas Dwellingan argument to the class constructor. residentsproperty is one varbecause the number of occupants may change after the instance is created.
abstract class Dwelling(private var residents: Int) {
    
    

Note that residentsattributes are marked with privatethe keyword . Private is a visibility modifier in Kotlin , indicating that residentsa property is only visible to (and can only be used within) this class. It cannot be accessed from elsewhere in the program. You can mark a property or method with the private keyword. Otherwise, if no visibility modifier is specified, the corresponding properties and methods default to public, accessible from other parts of the program. Since the number of occupants of a dwelling is usually private information (as opposed to information about building materials or occupancy), it is wise to mark it private.

With the and current occupants capacityof the house defined , you can create a function to determine if the house can accommodate additional occupants. You can define and implement the function in the class because the formula for calculating the availability of additional occupants is the same for all housing units. If the number of people is less than , it means that the corresponding can accommodate other guests, and the function should return or according to this comparison method .residentshasRoom()DwellinghasRoom()residentscapacityDwellingtruefalse

  1. Add hasRoom()the function to Dwellingthe class.
fun hasRoom(): Boolean {
    
    
    return residents < capacity
}
  1. You can run this code and there should be no errors. The code has not performed any visible operations.

The 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
   }
}

4. Create subclasses

Create a SquareCabin subclass

  1. DwellingUnder the class , create a SquareCabinclass called .
class SquareCabin
  1. Next, you need to indicate SquareCabinthat is Dwellingrelated to . In your code, you need to indicate SquareCabinthat extends from Dwelling(or is Dwellinga subclass of) because SquareCabinwill provide Dwellingthe implementation of the abstraction.

SquareCabinIndicate this inheritance by adding a colon ( :) after the class name, followed by a call to initialize the parent Dwellingclass . Don't forget to add parentheses after Dwellingthe class name.

class SquareCabin : Dwelling()
  1. If extending from a parent class, you must pass in the parameters required by the parent class. The number of peopleDwelling is required . residentsYou can pass in a fixed number of guests eg 3.
class SquareCabin : Dwelling(3)

However, you want your program to be more flexible and allow SquareCabinsfor varying numbers of guests. Therefore SquareCabin, residentsit will be used as a parameter in the class definition. Do not residentsdeclare as val, because you are reusing a property already declared Dwellingin .

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

Note: After using these class definitions, the system performs many operations behind the scenes.

In the class header you'll seeclass SquareCabin(residents: Int) ...

This is actually class SquareCabin constructor(residents: Int) ...a shorthand for

The system is called when you create an instance of an object through a class constructor. For example, SquareCabin(4)when , is
SquareCabincalled constructorto initialize the object instance.

constructorAn instance is constructed from all the information in the corresponding class, including the parameters passed in. When a class inherits properties and functions from a parent, constructor
the parent class is called constructorto complete the initialization of the object instance.

Therefore, when you SquareCabin(4)create an instance with , the system will SquareCabinexecute the constructor
constructor , due to the inheritance relationship . The class definition specifies that its constructor requires a parameter, so you 'll see it passed to the constructor in the class definition . In a later article, you'll learn more about constructors.DwellingDwelling
residentsSquareCabinDwelling
residents

  1. to run your code.
  2. This causes an error to occur. Let's take a look:
Class 'SquareCabin' is not abstract and does not implement abstract base class member public abstract val buildingMaterial: String defined in Dwelling

When you declare abstract functions and variables, you promise to provide them with values ​​and implementations later. For variables, this means that any subclasses of that abstract class need to provide it with a value. For functions, this means that any subclasses need to implement the function body.

In Dwellingthe class , you define a abstractvariable buildingMaterial. isSquareCabin a subclass of , so it must provide a value for . Use the keyword to indicate that this property is defined in the parent class and will be replaced in this classDwellingbuildingMaterialoverride

  1. In SquareCabinthe class , override buildingMaterialattribute and assign a value to it "Wood".
  2. capacityDo the same for and show that 6 people can live in a ` SquareCabin .
class SquareCabin(residents: Int) : Dwelling(residents) {
    
    
    override val buildingMaterial = "Wood"
    override val capacity = 6
}

The 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
   }
}

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

To test your code, create SquareCabinan instance .

Using Square Cabin

  1. Insert an empty function Dwellingand class definitions.SquareCabinmain()
fun main() {
    
    

}

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

class SquareCabin(residents: Int) : Dwelling(residents) {
    
    
    ...
}
  1. In main()the function , create an instancesquareCabin named with 6 guests . SquareCabinAdd output statements for building materials, capacity, and hasRoom()functions .
fun main() {
    
    
    val squareCabin = SquareCabin(6)

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

Note that hasRoom()the function is not defined in SquareCabinthe class , but in Dwellingthe class. Since SquareCabinis Dwellinga subclass of the class, hasRoom()functions are inherited directly without task operations. The function can SquareCabinnow be called on all instances of hasRoom(), as squareCabin.hasRoom()shown .

  1. Run the code, the output should look like this:
Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

You created with a squareCabinguest count of 6, which is equal to capacitythe value of , so hasRoom()returns false. You can try residentsinitializing SquareCabin, and when you run the program again, hasRoom() it should return true.

Use with to simplify code

In println()the statement , each squareCabintime a property or function of is referenced, it must be typed repeatedly squareCabin.. This creates duplication and can cause errors when you copy and paste output 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 use withthe statement to say "do all of the following with this instance object". Enter the keyword first with, then the instance name within parentheses, and then enter the curly brackets indicating what you want to do within them.

with (instanceName) {
    
    
    // all operations to do with instanceName
}
  1. In main()the function , change your output statement to use with.
  2. Remove the " " in the output statement squareCabin..
with(squareCabin) {
    
    
    println("\nSquare Cabin\n============")
    println("Capacity: ${
      
      capacity}")
    println("Material: ${
      
      buildingMaterial}")
    println("Has room? ${
      
      hasRoom()}")
}
  1. Run the code again to make sure it runs without any errors and outputs the same results.
Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

The completed code looks like this:

fun main() {
    
    
    val squareCabin = SquareCabin(6)

    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. Add another subclass for in SquareCabinthe same .DwellingRoundHut
  2. Replace buildingMaterialand give it the value " Straw".
    Replace capacityand set it to 4.
class RoundHut(residents: Int) : Dwelling(residents) {
    
    
    override val buildingMaterial = "Straw"
    override val capacity = 4
}
  1. main()In , create an RoundHutinstance of with 3 guests.
val roundHut = RoundHut(3)
  1. Add the following code to roundHutoutput information about .
with(roundHut) {
    
    
    println("\nRound Hut\n=========")
    println("Material: ${
      
      buildingMaterial}")
    println("Capacity: ${
      
      capacity}")
    println("Has room? ${
      
      hasRoom()}")
}
  1. Running the code, the output of the entire program should look like this:
Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

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

You now have a class hierarchy as shown below, where Dwellingis the root class SquareCabinand RoundHutis Dwellinga subclass of .

insert image description here

Create a RoundTower subclass

The final class in this class hierarchy is the circular tower. You can think of the circular tower as a multi-storey circular lodge built of stone. Therefore, you can RoundToweruse as RoundHuta subclass of .

  1. Create a RoundTowerclass that is RoundHuta subclass of . residentsAdd the parameter RoundTowerto the constructor of and pass that parameter to the constructor of RoundHutthe parent class.
  2. buildingMaterialReplace with "Stone".
  3. capacitySet to 4.
class RoundTower(residents: Int) : RoundHut(residents) {
    
    
    override val buildingMaterial = "Stone"
    override val capacity = 4
}
  1. Run this code and you'll get an error message.
This type is final, so it cannot be inherited from

This error means that RoundHutthe class cannot be subclassed (or inherited). By default, in Kotlin, classes are final and cannot be subclassed. You can only inherit from abstractclasses or openclasses marked with the keyword. Therefore, you need opento mark RoundHutthe class with the keyword so that it can be inherited.

Note: You do not need to use the keyword defining an abstract class . openFor example, there is no need opento mark Dwellingthe class with the keyword. The system already assumes that you need to subclass abstract classes to implement abstract properties and functions.

  1. Add the keyword at the beginning of RoundHutthe declaration open.
open class RoundHut(residents: Int) : Dwelling(residents) {
    
    
   override val buildingMaterial = "Straw"
   override val capacity = 4
}
  1. main()In , create an roundTowerinstance of and output information about it.
 val roundTower = RoundTower(4)
with(roundTower) {
    
    
    println("\nRound Tower\n==========")
    println("Material: ${
      
      buildingMaterial}")
    println("Capacity: ${
      
      capacity}")
    println("Has room? ${
      
      hasRoom()}")
}

Below is the complete code.

fun main() {
    
    
    val squareCabin = SquareCabin(6)
    val roundHut = RoundHut(3)
    val roundTower = RoundTower(4)

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

    with(roundHut) {
    
    
        println("\nRound Hut\n=========")
        println("Material: ${
      
      buildingMaterial}")
        println("Capacity: ${
      
      capacity}")
        println("Has room? ${
      
      hasRoom()}")
    }

    with(roundTower) {
    
    
        println("\nRound Tower\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
}

open class RoundHut(residents: Int) : Dwelling(residents) {
    
    
   override val buildingMaterial = "Straw"
   override val capacity = 4
}

class RoundTower(residents: Int) : RoundHut(residents) {
    
    
    override val buildingMaterial = "Stone"
    override val capacity = 4
}
  1. to run your code. It should now run without any errors and produce the following output.
Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

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

Round Tower
==========
Material: Stone
Capacity: 4
Has room? false

Add multiple floors to RoundTower

As the name suggests, RoundHutare single-story buildings, whereas towers often have multiple levels (floors).

Regarding capacity, the more floors a tower has, the more people it should be able to accommodate.

You can RoundTowermodify to have multiple floors and adjust the capacity based on the number of floors.

  1. Update the RoundTowerconstructor to accept another integer parameter val floorsas the number of floors. Put it residentsafter . Note that you don't need to pass this parameter to the parent RoundHutconstructor , because floorsis defined RoundTowerin and RoundHutdoesn't floors.
class RoundTower(
    residents: Int,
    val floors: Int) : RoundHut(residents) {
    
    

    ...
}

Note: If you have multiple parameters, and the class or function definition is too long to fit on one line, you can split it into multiple lines, as shown in the code above.

  1. to run your code. main()An error occurred while creating in the method roundTowerbecause you did not provide a number for floorsthe parameter . You can add missing parameters.

Alternatively, you RoundTowercan floorsadd a default value for in the class definition of as shown below. The system can then create object instances with default values ​​if no floorsvalue is passed to the constructor.

  1. In your code, add after floorsthe declaration = 2to assign it a default value of 2.
class RoundTower(
    residents: Int,
    val floors: Int = 2) : RoundHut(residents) {
    
    

    ...
}
  1. to run your code. It should compile without a hitch, RoundTower(4)since RoundTowerthe object instance will now be created with the default value of 2 floors.
  2. In RoundTowerthe class , update capacity, multiply the number of floors by the previous value.
override val capacity = 4 * floors
  1. Run the code and notice that RoundTowerthe occupancy is now 8 people (2 floors).

Below is the finished code.

fun main() {
    
    

    val squareCabin = SquareCabin(6)
    val roundHut = RoundHut(3)
    val roundTower = RoundTower(4)

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

    with(roundHut) {
    
    
        println("\nRound Hut\n=========")
        println("Material: ${
      
      buildingMaterial}")
        println("Capacity: ${
      
      capacity}")
        println("Has room? ${
      
      hasRoom()}")
    }

    with(roundTower) {
    
    
        println("\nRound Tower\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
}

open class RoundHut(residents: Int) : Dwelling(residents) {
    
    
    override val buildingMaterial = "Straw"
    override val capacity = 4
}

class RoundTower(
    residents: Int,
    val floors: Int = 2) : RoundHut(residents) {
    
    

    override val buildingMaterial = "Stone"
    override val capacity = 4 * floors
}

5. Modify the classes in the hierarchy

Calculate floor area

In this exercise, you will learn how to declare an abstract function in an abstract class and then implement its function in a subclass.

All dwellings have floor area, but how it is calculated varies by dwelling shape.

Define floorArea() in the Dwelling class

  1. First Dwelling, add a abstract floorArea()function to the class. return Double. Double, like Stringand Int, is a data type; it can be used for floating-point numbers (that is, numbers with a decimal point followed by a fractional part, such as 5.8793).
abstract fun floorArea(): Double

All abstract methods defined in an abstract class must be implemented in any of its subclasses. You need to implement it in your subclass floorArea()before you can run your code.

Implement floorArea() for SquareCabin

As with implementing buildingMaterialand capacity, since abstractthe function is defined in the parent class, you need to use overridethe keyword.

  1. In SquareCabinthe class , enter the keyword first override, followed by the actual implementation of floorArea()the function , as shown below.
override fun floorArea(): Double {
    
    

}
  1. Returns the calculated building area. The area of ​​a rectangle or square is calculated by multiplying the length of one side by the length of the other side. The function body is return length * length.
override fun floorArea(): Double {
    
    
    return length * length
}

In this class, the length is a variable that varies from instance to instance, so you can add it as a constructor argument to SquareCabinthe class .

  1. SquareCabinChange the class definition of to add a parameterDouble of type . lengthDeclare this property as val, since the length of the building will not vary.
class SquareCabin(residents: Int, val length: Double) : Dwelling(residents) {
    
    

DwellingHas a constructor parameter residents, so all its subclasses have that parameter. Since this is the first parameter in Dwellingthe constructor , it's a good idea to also make it the first parameter in all subclass constructors, with the parameters in the same order in all class definitions. Therefore residents, a new lengthparameter is inserted after the parameter.

  1. main()In , update squareCabinthe code that creates an instance of . Will 50.0be lengthpassed as SquareCabinconstructor .
val squareCabin = SquareCabin(6, 50.0)
  1. squareCabinIn withthe statement for , add an output statement for Floor Area.
println("Floor area: ${
      
      floorArea()}")

Your code won't run because you also need to implement RoundHutin floorArea().

Implement floorArea() for RoundHut

RoundHutRealize the building area for in the same way . RoundHutis Dwellingalso a direct subclass of , so you need to use overridethe keyword.

The calculation method of circular housing building area is, PI * radius * radius.

PIis a mathematical value. It will be defined in the math library. A library is a predefined collection of functions and values, defined outside a program, but available to the program. In order to use a library function or value, you need to tell the compiler that you will use that function or value. The method is to import the function or value into the program. To use it in a program PI, you need to import it kotlin.math.PI.

  1. Imported from the Kotlin math library PI. Put this import statement at the top of the file, main()before .
import kotlin.math.PI
  1. RoundHutImplement floorArea()the function for .
override fun floorArea(): Double {
    
    
    return PI * radius * radius
}

Warning: If you don't import kotlin.math.PI , you will encounter errors, so please import this library before using it. Alternatively, you can write out the fully qualified version of PI, as in "kotlin.math.PI * radius * radius", then you don't need to add import statements.

  1. Update the RoundHutconstructor to pass in radius.
open class RoundHut(
   residents: Int,
   val radius: Double) : Dwelling(residents) {
    
    
  1. main()In , update the initialization of by RoundHutpassing in the constructor .radius 10.0roundHut
val roundHut = RoundHut(3, 10.0)
  1. roundHutAdd an output statement to the statement of the with.
println("Floor area: ${
      
      floorArea()}")

Implement floorArea() for RoundTower

Your code doesn't work yet, it fails with the following error message:

Error: No value passed for parameter 'radius'

RoundTowerIn , you don't need to implement in order for the program to compile successfully floorArea(), since it RoundHutinherits , but you need to update RoundTowerthe class definition to have RoundHutthe same radius.

  1. Change the RoundTower constructor to accept the same radius. radiusPut residentsafter, floorsbefore. It is recommended to list variables with default values ​​at the end. Remember to radiuspass to the parent class constructor.
class RoundTower(
    residents: Int,
    radius: Double,
    val floors: Int = 2) : RoundHut(residents, radius) {
    
    
  1. main()Update the code that initializes in roundTower.
val roundTower = RoundTower(4, 15.5)
  1. Add an output statement floorArea()that .
println("Floor area: ${
      
      floorArea()}")
  1. Now you can run your code!
  2. Note that RoundTowerthe calculation of is incorrect because it inherits from RoundHut, and does not take into account floorsthe quantity .
  3. RoundTowerIn , override floorArea()to give it a different implementation, multiply the area by the number of floors. Note that you can Dwellingdefine a function in an abstract class ( ), implement it in a subclass ( ), and then replace it again in RoundHuta subclass of the subclass ( ). RoundTowerThis is the best of both worlds, you can inherit the functions you need and replace the functions you don't need.
override fun floorArea(): Double {
    
    
    return PI * radius * radius * floors
}

This code works fine, but there is a way to avoid reusing code already RoundHutin . You can call the function RoundHutfrom floorArea(), which returns it PI * radius * radius. Then multiply that result by floorsthe number .

  1. RoundTowerIn , floorArea()update to use floorArea()the implementation . Using superthe keyword , a function defined in the parent is called.
override fun floorArea(): Double {
    
    
    return super.floorArea() * floors
}
  1. Running your code again RoundTowerwill output the correct floor area for the corresponding number of floors.

is the finished code:

import kotlin.math.PI

fun main() {
    
    

    val squareCabin = SquareCabin(6, 50.0)
    val roundHut = RoundHut(3, 10.0)
    val roundTower = RoundTower(4, 15.5)

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

    with(roundHut) {
    
    
        println("\nRound Hut\n=========")
        println("Material: ${
      
      buildingMaterial}")
        println("Capacity: ${
      
      capacity}")
        println("Has room? ${
      
      hasRoom()}")
        println("Floor area: ${
      
      floorArea()}")
    }

    with(roundTower) {
    
    
        println("\nRound Tower\n==========")
        println("Material: ${
      
      buildingMaterial}")
        println("Capacity: ${
      
      capacity}")
        println("Has room? ${
      
      hasRoom()}")
        println("Floor area: ${
      
      floorArea()}")
    }
 }

abstract class Dwelling(private var residents: Int) {
    
    

    abstract val buildingMaterial: String
    abstract val capacity: Int

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

    abstract fun floorArea(): Double
}

class SquareCabin(residents: Int,
    val length: Double) : Dwelling(residents) {
    
    

    override val buildingMaterial = "Wood"
    override val capacity = 6

    override fun floorArea(): Double {
    
    
       return length * length
    }
}

open class RoundHut(residents: Int,
    val radius: Double) : Dwelling(residents) {
    
    

    override val buildingMaterial = "Straw"
    override val capacity = 4

    override fun floorArea(): Double {
    
    
        return PI * radius * radius
    }
}

class RoundTower(residents: Int, radius: Double,
    val floors: Int = 2) : RoundHut(residents, radius) {
    
    

    override val buildingMaterial = "Stone"
    override val capacity = 4 * floors

    override fun floorArea(): Double {
    
    
        return super.floorArea() * floors
    }
}

The output should look like this:

Square Cabin
============
Capacity: 6
Material: Wood
Has room? false
Floor area: 2500.0

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

Round Tower
==========
Material: Stone
Capacity: 8
Has room? true
Floor area: 1509.5352700498956

Note: For area values, the user experience is better if only 2 decimal places are displayed. That's outside the scope of this article, but you can use the following statement to output floor area:println("Floor area: %.2f".format(floorArea()))

Allow new guests to get room

A room is made available to new guests using getRoom()the function , which increments the number of guests by 1. Since this logic applies to all housings, you can implement the function Dwellingin so that it works for all subclasses and their children. marvelous!

Notice:

  • Use ifthe statement to only add guests if the capacity limit is not reached.
  • Print a message on the result.
  • You can use residents++as residents = residents + 1a shorthand for incrementing residentsthe variable 1.
  1. Implement the function in Dwellingthe class getRoom().
fun getRoom() {
    
    
    if (capacity > residents) {
    
    
        residents++
        println("You got a room!")
    } else {
    
    
        println("Sorry, at capacity and no rooms left.")
    }
}

Tip: In the code block above, you can replace ifthe condition to use hasRoom()the function - if(hasRoom()) {...}. hasRoom()The function defined above compares the capacity to the number of guests.

  1. Add some output statements roundHutto the statement block of to use both and to observe changes.withgetRoom()hasRoom()
println("Has room? ${
      
      hasRoom()}")
getRoom()
println("Has room? ${
      
      hasRoom()}")
getRoom()

The output of these output statements is as follows:

Has room? true
You got a room!
Has room? false
Sorry, at capacity and no rooms left.

See the solution code for details.

Note: This is an example to illustrate why inheritance is so powerful. You Dwellingcan call getRoom()the function on all subclasses of without adding extra code to those classes.

Getting the Right Rug for a Round Housing

Let's say you need to know what side rug to get for your RoundHutor . RoundTowerPut the corresponding function RoundHutin to make it available for all circular housings.

insert image description here

  1. First kotlin.math, import sqrt()the function from the library.
import kotlin.math.sqrt
  1. Implement the function in RoundHutthe class calculateMaxCarpetLength(). The formula for calculating the length of a square rug that fits in a circle is sqrt(2) * radius. The figure above illustrates this.
fun calculateMaxCarpetLength(): Double {
    
    

    return sqrt(2.0) * radius
}

Passing Doublevalue 2.0into a math function sqrt(2.0)because the function's return type is Doublenot Integer.

  1. calculateMaxCarpetLength()Methods RoundHutcan now RoundTowerbe called on and instances. In main()the function , addroundHut output statements for and .roundTower
println("Carpet Length: ${
      
      calculateMaxCarpetLength()}")

See the solution code for details.

Note: you calculateMaxCarpetLength()added to RoundHut, RoundTowerwhich inherits the function. However, you cannot call this function on squareCabinan instance , because squareCabin does not define this function, nor does it define its parent class.

Congratulations! You've created a complete class hierarchy with properties and functions, and learned everything you need to know to create more utility classes!

6. Solution code

Below is the complete solution code for this post, including comments.

/**
* 为不同类型的住宅实现类的程序。
*演示实现:
* 创建具有继承性的类层次结构、变量和函数,
* 抽象类、重写和私有变量与公共变量。
*/

import kotlin.math.PI
import kotlin.math.sqrt

fun main() {
    
    
   val squareCabin = SquareCabin(6, 50.0)
   val roundHut = RoundHut(3, 10.0)
   val roundTower = RoundTower(4, 15.5)

   with(squareCabin) {
    
    
       println("\nSquare Cabin\n============")
       println("Capacity: ${
      
      capacity}")
       println("Material: ${
      
      buildingMaterial}")
       println("Floor area: ${
      
      floorArea()}")
   }

   with(roundHut) {
    
    
       println("\nRound Hut\n=========")
       println("Material: ${
      
      buildingMaterial}")
       println("Capacity: ${
      
      capacity}")
       println("Floor area: ${
      
      floorArea()}")
       println("Has room? ${
      
      hasRoom()}")
       getRoom()
       println("Has room? ${
      
      hasRoom()}")
       getRoom()
       println("Carpet size: ${
      
      calculateMaxCarpetLength()}")
   }

   with(roundTower) {
    
    
       println("\nRound Tower\n==========")
       println("Material: ${
      
      buildingMaterial}")
       println("Capacity: ${
      
      capacity}")
       println("Floor area: ${
      
      floorArea()}")
       println("Carpet Length: ${
      
      calculateMaxCarpetLength()}")
   }
}

/**
* 定义所有住宅共有的属性。
* 所有的住宅都有楼面面积,
* 但它的计算方式是特定于子类的。
* 在这里实现检查和获得一个房间
* 因为它们对于所有的住宅子类都是一样的。
*
* @param residents 目前居民的数量
*/
abstract class Dwelling(private var residents: Int) {
    
    
   abstract val buildingMaterial: String
   abstract val capacity: Int

   /**
    * 计算住宅的建筑面积。
    * 由确定子类的形状实现。
    *
    * @return 楼层面积
    */
   abstract fun floorArea(): Double

   /**
    * 检查是否有其他居民的空间。
    *
    * @return true 如果还有房间的话, false 如果没有房间的话
    */
   fun hasRoom(): Boolean {
    
    
       return residents < capacity
   }

   /**
    * 将容量与居民人数进行比较
    * 如果容量大于居民人数
    * 通过增加居民人数来增加居民。
    * 打印结果
    */
   fun getRoom() {
    
    
       if (capacity > residents) {
    
    
           residents++
           println("You got a room!")
       } else {
    
    
           println("Sorry, at capacity and no rooms left.")
       }
   }

   }

/**
* 一个方形的小屋住宅。
*
*  @param residents 目前居民人数
*  @param length 长度
*/
class SquareCabin(residents: Int, val length: Double) : Dwelling(residents) {
    
    
   override val buildingMaterial = "Wood"
   override val capacity = 6

   /**
    * 计算方形住宅的面积。
    *
    * @return 楼层面积
    */
   override fun floorArea(): Double {
    
    
       return length * length
   }

}

/**
* 圆楼面面积住宅
*
* @param residents 目前居民的数量
* @param radius 半径
*/
open class RoundHut(
       residents: Int, val radius: Double) : Dwelling(residents) {
    
    

   override val buildingMaterial = "Straw"
   override val capacity = 4

   /**
    * 计算圆形住宅的面积。
    *
    * @return 楼层面积
    */
   override fun floorArea(): Double {
    
    
       return PI * radius * radius
   }

   /**
    *  计算方形地毯的最大长度
    *  适合圆形的地板
    *
    * @return 方形地毯的长度
    */
    fun calculateMaxCarpetLength(): Double {
    
    
        return sqrt(2.0) * radius
    }

}

/**
 * 多层圆塔
 *  * @param residents 目前居民的数量
 * @param radius 半径
 * @param floors 楼层数
*/
class RoundTower(
       residents: Int,
       radius: Double,
       val floors: Int = 2) : RoundHut(residents, radius) {
    
    

   override val buildingMaterial = "Stone"

   // 容量取决于层数。
   override val capacity = floors * 4

   /**
    * 计算塔式住宅的总建筑面积
    *多层面积和
    *
    * @return 楼面面积
    */
   override fun floorArea(): Double {
    
    
       return super.floorArea() * floors
   }
}

7. Summary

In this article, you learned how to:

  • Creates a class hierarchy, which is a tree structure containing classes where children inherit functions from parent classes. Subclasses inherit properties and functions.

  • Create a abstractclass in which some functions are left to subclasses to implement. Therefore, abstractthe class cannot be instantiated.

  • Create a subclass of abstractthe class .

  • Use overridethe keyword to replace properties and functions in subclasses.

  • Use superthe keyword to refer to functions and properties in the parent class.

  • Marks a class openas enabling it to be subclassed.

  • Marks a property so privatethat it can only be used within the corresponding class.

  • Use the withconstructor to make multiple calls on the same object instance.

  • import function from kotlin.mathlibrary

8. Learn more

Guess you like

Origin blog.csdn.net/Jasonhso/article/details/126001793