1. Before you begin
What you'll learn
- Create a Kotlin program that uses inheritance to implement a hierarchy of classes.
- Extend a class, override its existing functionality, and add new functionality.
- Choose the correct visibility modifier for variables.
What you'll build
- A Kotlin program with different types of dwellings that are implemented as a class hierarchy.
2. What is a class hierarchy?
Vegetable a class in Kotlin, you can create Legume as a child or subclass of the Vegetable class. That means all the properties and methods of the Vegetable class are inherited by (meaning also available in) the Legume class.Vegetable as the parent or superclass of the Legume class.
You could continue and expand the class hierarchy by creating subclasses of Legume such as Lentil and Chickpea. This makes Legume both a child or subclass of Vegetable as well as a parent or superclass of Lentil and Chickpea. Vegetable is the root or *top-level (*or base) class of this hierarchy.

Inheritance in Android Classes
View class in Android that represents a rectangular area on the screen and is responsible for drawing and event handling. The TextView class is a subclass of the View class, which means that TextView inherits all the properties and functionality from the View class, plus adds specific logic for displaying text to the user.
Taking it a step further, the EditText and Button classes are children of the TextView class. They inherit all the properties and methods of the TextView and View classes, plus add their own specific logic. For example, EditText adds its own functionality of being able to edit text on the screen.
Instead of having to copy and paste all the logic from the View and TextView classes into the EditText class, the EditText can just subclass the TextView class, which in turn subclasses the View class. Then the code in the EditText class can focus specifically on what makes this UI component different from other views.
On the top of a documentation page for an Android class on the developer.android.com website, you can see the class hierarchy diagram. If you see kotlin.Any at the top of hierarchy, it's because in Kotlin, all classes have a common superclass Any. Learn more here.

As you can see, learning to leverage inheritance among classes can make your code easier to write, reuse, read, and test.
3. Create a base class
Class hierarchy of dwellings
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 ofRoundTower.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.
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: StringProperty must be initialized or be abstract
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.abstract keyword onto the variable definition.abstract val buildingMaterial: StringMake an instance of
Dwelling in the main() function and run your code.val dwelling = Dwelling()Dwelling class.Cannot create an instance of an abstract class
abstract class Dwelling(){
abstract val buildingMaterial: String
}Add a property for capacity
Another property of a dwelling is the capacity, that is, how many people can live in it.
All dwellings have a capacity that doesn't change. However, the capacity cannot be set within the Dwelling superclass. It should be defined in subclasses for specific types of dwellings.
- In
Dwelling, add anabstractintegervalcalledcapacity.
abstract val capacity: IntAdd 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.
- You can make
residentsa parameter that is passed into the constructor of theDwellingclass. Theresidentsproperty is avar, because the number of residents can change after the instance has been created.
abstract class Dwelling(private var residents: Int) {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.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.- Add the
hasRoom()function to theDwellingclass.
fun hasRoom(): Boolean {
return residents < capacity
}- 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
}
}4. Create subclasses
Create a SquareCabin subclass
- Below the
Dwellingclass, create a class calledSquareCabin.
class SquareCabin
- In your code, you want to indicate that
SquareCabinextends fromDwelling(or is a subclass toDwelling)becauseSquareCabinwill provide an implementation for the abstract parts ofDwelling.
:) 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()- When extending from a superclass, you must pass in the required parameters expected by the superclass.
Dwellingrequires the number ofresidentsas input. You could pass in a fixed number of residents like3.
class SquareCabin : Dwelling(3)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)- Run your code.
- This will cause errors. Take a look:
Class 'SquareCabin' is not abstract and does not implement abstract base class member public abstract val buildingMaterial: String defined in Dwelling
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.- Inside the
SquareCabinclass,overridethebuildingMaterialproperty and assign it the value"Wood". - Do the same for the
capacity, saying 6 residents can live in aSquareCabin.
class SquareCabin(residents: Int) : Dwelling(residents) {
override val buildingMaterial = "Wood"
override val capacity = 6
}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
}SquareCabin in your program.Use SquareCabin
- Insert an empty
main()function before theDwellingandSquareCabinclass definitions.
fun main() {
}
abstract class Dwelling(private var residents: Int) {
...
}
class SquareCabin(residents: Int) : Dwelling(residents) {
...
}- Within the
main()function, create an instance ofSquareCabincalledsquareCabinwith 6 residents. Add print statements for the building material, the capacity, and thehasRoom()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().
- Run your code, and it should print the following.
Square Cabin ============ Capacity: 6 Material: Wood Has room? false
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
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.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.with (instanceName) {
// all operations to do with instanceName
}- In the
main()function, change your print statements to usewith. - Delete
squareCabin.in the print statements.
with(squareCabin) {
println("\nSquare Cabin\n============")
println("Capacity: ${capacity}")
println("Material: ${buildingMaterial}")
println("Has room? ${hasRoom()}")
}- Run your code again to make sure it runs without errors and shows the same output.
Square Cabin ============ Capacity: 6 Material: Wood Has room? false
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
- In the same way as the
SquareCabin, add another subclass,RoundHut, toDwelling. - Override
buildingMaterialand give it a value of"Straw". - Override
capacityand set it to 4.
class RoundHut(residents: Int) : Dwelling(residents) {
override val buildingMaterial = "Straw"
override val capacity = 4
}- In
main(), create an instance ofRoundHutwith 3 residents.
val roundHut = RoundHut(3)- 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()}")
}- 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.
- Create a
RoundTowerclass that is a subclass ofRoundHut. Add theresidentsparameter to the constructor ofRoundTower, and then pass that parameter to the constructor of theRoundHutsuperclass. - Override the
buildingMaterialto be"Stone". - Set the
capacityto4.
class RoundTower(residents: Int) : RoundHut(residents) {
override val buildingMaterial = "Stone"
override val capacity = 4
}- Run this code and you get an error.
This type is final, so it cannot be inherited from
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.- Add the
openkeyword at the start of theRoundHutdeclaration.
open class RoundHut(residents: Int) : Dwelling(residents) {
override val buildingMaterial = "Straw"
override val capacity = 4
}- In
main(), create an instance ofroundTowerand print information about it.
val roundTower = RoundTower(4)with(roundTower) {
println("\nRound Tower\n==========")
println("Material: ${buildingMaterial}")
println("Capacity: ${capacity}")
println("Has room? ${hasRoom()}")
}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
}- Run your code. It should now work without 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
RoundHut, by implication, is a single-story building. Towers usually have multiple stories (floors).RoundTower to have multiple floors, and adjust its capacity based on the number of floors.- Update the
RoundTowerconstructor to take an additional integer parameterval floorsfor the number of floors. Put it afterresidents. Notice that you don't need to pass this to the parentRoundHutconstructor becausefloorsis defined here inRoundTowerandRoundHuthas nofloors.
class RoundTower(
residents: Int,
val floors: Int) : RoundHut(residents) {
...
}- Run your code. There is an error when creating
roundTowerin themain()method, because you are not supplying a number for thefloorsargument. You could add the missing argument.
RoundTower, you can add a default value for floors as shown below. Then, when no value for floors is passed into the constructor, the default value can be used to create the object instance.- In your code, add
= 2after the declaration offloorsto assign it a default value of 2.
class RoundTower(
residents: Int,
val floors: Int = 2) : RoundHut(residents) {
...
}- Run your code. It should compile because
RoundTower(4)now creates aRoundTowerobject instance with the default value of 2 floors. - In the
RoundTowerclass, update thecapacityto multiply it by the number of floors.
override val capacity = 4 * floors- Run your code and notice that the
RoundTowercapacity is now 8 for 2 floors.
Here is your 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 classes in the hierarchy
Calculate the floor area
In this exercise, you will learn how you can declare an abstract function in an abstract class and then implement its functionality in the subclasses.
Define floorArea() in Dwelling class
- First add an
abstractfloorArea()function to theDwellingclass. Return aDouble. Double is a data type, likeStringandInt; it is used for floating point numbers, that is, numbers that have a decimal point followed by a fractional part, such as 5.8793.)
abstract fun floorArea(): DoublefloorArea() in the subclasses.Implement floorArea() for SquareCabin
Like with buildingMaterial and capacity, since you are implementing an abstract function that's defined in the parent class, you need to use the override keyword.
- In the
SquareCabinclass, start with the keywordoverridefollowed by the actual implementation of thefloorArea()function as shown below.
override fun floorArea(): Double {
}- Return the calculated floor area. The area of a rectangle or square is the length of its side multiplied by the length of its other side. The body of the function will
return length * length.
override fun floorArea(): Double {
return length * length
}SquareCabin class.- Change the class definition of
SquareCabinto add alengthparameter of typeDouble. Declare the property as avalbecause the length of a building doesn't change.
class SquareCabin(residents: Int, val length: Double) : Dwelling(residents) {Dwelling and therefore all its subclasses have residents as a constructor argument. Because it's the first argument in the Dwelling constructor, It is a best practice to also make it the first argument in all subclass constructors and put the arguments in the same order in all the class definitions. Hence insert the new length parameter after the residents parameter.
- In
main()update the creation of thesquareCabininstance. Pass in50.0to theSquareCabinconstructor as thelength.
val squareCabin = SquareCabin(6, 50.0)- Inside the
withstatement forsquareCabin, add a print statement for the floor area.
println("Floor area: ${floorArea()}")Implement floorArea() for RoundHut
RoundHut. RoundHut is also a direct subclass of Dwelling, so you need to use the override keyword.PI is a mathematical value. It is defined in a math library. A library is a predefined collection of functions and values defined outside a program that a program can use. In order to use a library function or value, you need to tell the compiler that you are going to use it. You do this by importing the function or value into your program. To use PI in your program, you need to import kotlin.math.PI.- Import
PIfrom the Kotlin math library. Put this at the top of the file, beforemain().
import kotlin.math.PI- Implement the
floorArea()function forRoundHut.
override fun floorArea(): Double {
return PI * radius * radius
}kotlin.math.PI
PIkotlin.math.PI * radius * radius- Update the
RoundHutconstructor to pass in theradius.
open class RoundHut(
val residents: Int,
val radius: Double) : Dwelling(residents) {- In
main(), update the initialization ofroundHutby passing in aradiusof10.0to theRoundHutconstructor.
val roundHut = RoundHut(3, 10.0)- Add a print statement inside the
withstatement forroundHut.
println("Floor area: ${floorArea()}")Implement floorArea() for RoundTower
Your code doesn't run yet, and fails with this error:
Error: No value passed for parameter 'radius'
In RoundTower, in order for your program to compile, you don't need to implement floorArea() as it gets inherited from RoundHut, but you need to update the RoundTower class definition to also have the same radius argument as its parent RoundHut.
- Change the constructor of RoundTower to also take the
radius. Put theradiusafterresidentsand beforefloors. It is recommended that variables with default values are listed at the end. Remember to passradiusto the parent class constructor.
class RoundTower(
residents: Int,
radius: Double,
val floors: Int = 2) : RoundHut(residents, radius) {- Update the initialization of
roundTowerinmain().
val roundTower = RoundTower(4, 15.5)- And add a print statement that calls
floorArea().
println("Floor area: ${floorArea()}")- You can now run your code!
- Notice that the calculation for the
RoundToweris not correct, because it is inherited fromRoundHutand does not take into account the number offloors. - In
RoundTower,override floorArea()so you can give it a different implementation that multiplies the area with the number of floors. Notice how you can define a function in an abstract class (Dwelling), implement it in a subclass (RoundHut) and then override it again in a subclass of the subclass (RoundTower). It's the best of both worlds - you inherit the functionality you want, and can override the functionality you don't want.
override fun floorArea(): Double {
return PI * radius * radius * floors
}RoundHut parent class. You can call the floorArea() function from the parent RoundHut class, which returns PI * radius * radius. Then multiply that result by the number of floors.- In
RoundTower, updatefloorArea()to use the superclass implementation offloorArea(). Use thesuperkeyword to call the function that is defined in the parent.
override fun floorArea(): Double {
return super.floorArea() * floors
}- Run your code again and
RoundToweroutputs the correct floor space for multiple floors.
Here is your 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(val 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
}
}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
Allow a new resident to get a room
getRoom() function that increases the number of residents by one. Since this logic is the same for all dwellings, you can implement the function in Dwelling, and this makes it available to all subclasses and their children. Neat!Notes:
- Use an
ifstatement that only adds a resident if there is capacity left. - Print a message for the outcome.
- You can use
residents++as a shorthand forresidents = residents + 1to add 1 to theresidentsvariable.
- Implement the
getRoom()function in theDwellingclass.
fun getRoom() {
if (capacity > residents) {
residents++
println("You got a room!")
} else {
println("Sorry, at capacity and no rooms left.")
}
}- Add some print statements to the
withstatement block forroundHutto observe what happens withgetRoom()andhasRoom()used together.
println("Has room? ${hasRoom()}")
getRoom()
println("Has room? ${hasRoom()}")
getRoom()Has room? true You got a room! Has room? false Sorry, at capacity and no rooms left.
See Solution code for details.
Fit a carpet into a round dwelling
RoundHut or RoundTower. For the SquareCabin, the max carpet is the same as the floor area from the length, so no extra calculations are needed. Put the function into RoundHut to make it available to all round dwellings.- First import the
sqrt()function from thekotlin.mathlibrary.
import kotlin.math.sqrt- Implement the
calculateMaxCarpetSize()function in theRoundHutclass. The formula for fitting a rectangle into a circle is the square root of the diameter squared divided by 2:sqrt (diameter * diameter / 2)
fun calculateMaxCarpetSize(): Double {
val diameter = 2 * radius
return sqrt(diameter * diameter / 2)
}- The
calculateMaxCarpetSize()method can now be called onRoundHutandRoundTowerinstances. Add print statements toroundHutandroundTowerin themain()function.
println("Carpet size: ${calculateMaxCarpetSize()}")See Solution code for details.
Congratulations! You have created a complete class hierarchy with properties and functions, learning everything you need to create more useful classes!
6. Solution code
This is the complete solution code for this codelab, including comments.
/**
* Program that implements classes for different kinds of dwellings.
* Shows how to:
* Create class hierarchy, variables and functions with inheritance,
* abstract class, overriding, and private vs. public variables.
*/
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: ${calculateMaxCarpetSize()}")
}
with(roundTower) {
println("\nRound Tower\n==========")
println("Material: ${buildingMaterial}")
println("Capacity: ${capacity}")
println("Floor area: ${floorArea()}")
println("Carpet size: ${calculateMaxCarpetSize()}")
}
}
/**
* Defines properties common to all dwellings.
* All dwellings have floorspace,
* but its calculation is specific to the subclass.
* Checking and getting a room are implemented here
* because they are the same for all Dwelling subclasses.
*
* @param residents Current number of residents
*/
abstract class Dwelling(private var residents: Int) {
abstract val buildingMaterial: String
abstract val capacity: Int
/**
* Calculates the floor area of the dwelling.
* Implemented by subclasses where shape is determined.
*
* @return floor area
*/
abstract fun floorArea(): Double
/**
* Checks whether there is room for another resident.
*
* @return true if room available, false otherwise
*/
fun hasRoom(): Boolean {
return residents < capacity
}
/**
* Compares the capacity to the number of residents and
* if capacity is larger than number of residents,
* add resident by increasing the number of residents.
* Print the result.
*/
fun getRoom() {
if (capacity > residents) {
residents++
println("You got a room!")
} else {
println("Sorry, at capacity and no rooms left.")
}
}
}
/**
* A square cabin dwelling.
*
* @param residents Current number of residents
* @param length Length
*/
class SquareCabin(residents: Int, val length: Double) : Dwelling(residents) {
override val buildingMaterial = "Wood"
override val capacity = 6
/**
* Calculates floor area for a square dwelling.
*
* @return floor area
*/
override fun floorArea(): Double {
return length * length
}
}
/**
* Dwelling with a circular floorspace
*
* @param residents Current number of residents
* @param radius Radius
*/
open class RoundHut(
val residents: Int, val radius: Double) : Dwelling(residents) {
override val buildingMaterial = "Straw"
override val capacity = 4
/**
* Calculates floor area for a round dwelling.
*
* @return floor area
*/
override fun floorArea(): Double {
return PI * radius * radius
}
/**
* Calculates the max length for a square carpet
* that fits the circular floor.
*
* @return length of carpet
*/
fun calculateMaxCarpetSize(): Double {
val diameter = 2 * radius
return sqrt(diameter * diameter / 2)
}
}
/**
* Round tower with multiple stories.
*
* @param residents Current number of residents
* @param radius Radius
* @param floors Number of stories
*/
class RoundTower(
residents: Int,
radius: Double,
val floors: Int = 2) : RoundHut(residents, radius) {
override val buildingMaterial = "Stone"
// Capacity depends on the number of floors.
override val capacity = floors * 4
/**
* Calculates the total floor area for a tower dwelling
* with multiple stories.
*
* @return floor area
*/
override fun floorArea(): Double {
return super.floorArea() * floors
}
}7. Summary
In this codelab you learned how to:
- Create a class hierarchy, that is a tree of classes where children inherit functionality from parent classes. Properties and functions are inherited by subclasses.
- Create an
abstractclass where some functionality is left to be implemented by its subclasses. Anabstractclass can therefore not be instantiated. - Create subclasses of an
abstractclass. - Use
overridekeyword to override properties and functions in subclasses. - Use the
superkeyword to reference functions and properties in the parent class. - Make a class
openso that it can be subclassed. - Make a property
private, so it can only be used inside the class. - Use the
withconstruct to make multiple calls on the same object instance. - Import functionality from the
kotlin.mathlibrary

0 Comments