官术网_书友最值得收藏!

  • Functional Kotlin
  • Mario Arias Rivu Chakraborty
  • 515字
  • 2021-06-24 19:15:22

Abstract classes

So far, so good. Our bakery looks good. However, we have a problem with our current model. Let's look at the following code:

fun main(args: Array<String>) {
val anyGood = BakeryGood("Generic flavour")
}

We can instantiate the BakeryGood class directly, which is too generic. To correct this situation, we can mark BakeryGood as abstract:

abstract class BakeryGood(val flavour: String) { 
fun eat(): String {
return "nom, nom, nom... delicious $flavour ${name()}"
}

open fun name(): String {
return "bakery good"
}
}

An abstract class is a class designed solely to be extended. An abstract class can't be instantiated, which fixes our problem.

What makes abstract different from open?

Both modifiers let us extend a class, but open lets us instantiate while abstract does not.

Now that we can't instantiate, our name() method in the BakeryGood class isn't that useful anymore, and all our subclasses, except for CinnamonRoll, override it anyway (CinnamonRoll relays on the Roll implementation):

abstract class BakeryGood(val flavour: String) { 
fun eat(): String {
return "nom, nom, nom... delicious $flavour ${name()}"
}

abstract fun name(): String
}

A method marked as abstract doesn't have a body, just the signature declaration (a method signature is a way to identify a method). In Kotlin, a signature is composed of the method's name, its number, the type of parameters, and the return type.

Any class that extends BakeryGood directly must override the name() method. The technical term for overriding an abstract method is implement and, from now on, we will use it. So, the Cupcake class implements the name() method (Kotlin doesn't have a keyword for method implementation; both cases, method implementation, and method overriding, use the keyword override).

Let's introduce a new class, Customer; a bakery needs customers anyway:

class Customer(val name: String) {
fun eats(food: BakeryGood) {
println("$name is eating... ${food.eat()}")
}
}

fun main(args: Array<String>) {
val myDonut = Donut("Custard", "Powdered sugar")
val mario = Customer("Mario")
mario.eats(myDonut)
}

The eats(food: BakeryGood) method takes a BakeryGood parameter, so any instance of any class that extends the BakeryGood parameter, it doesn't matter how many hierarchy levels. Just remember that we can instantiate BakeryGood directly.

What happens if we want a simple BakeryGood? For example, testing.

There is an alternative, an anonymous subclass:

fun main(args: Array<String>) {
val mario = Customer("Mario")

mario.eats(object : BakeryGood("TEST_1") {
override fun name(): String {
return "TEST_2"
}
})
}

A new keyword is introduced here, object. Later on, we'll cover object in more detail, but for now, it is enough to know that this is an object expression. An object expression defines an instance of an anonymous class that extends a type.

In our example, the object expression (technically, the anonymous class) must override the name() method and pass a value as the parameter for the BakeryGood constructor, exactly as a standard class would do.

Remember that an object expression is an instance, so it can be used to declare values:

val food: BakeryGood = object : BakeryGood("TEST_1") { 
override fun name(): String {
return "TEST_2"
}
}

mario.eats(food)
主站蜘蛛池模板: 吉木萨尔县| 邯郸县| 荥阳市| 建水县| 呼图壁县| 阜宁县| 丹阳市| 崇明县| 客服| 疏附县| 元朗区| 福鼎市| 澳门| 郓城县| 磐石市| 扎赉特旗| 渑池县| 离岛区| 新昌县| 曲阳县| 通山县| 万盛区| 东丰县| 木兰县| 分宜县| 文昌市| 建平县| 同仁县| 望都县| 台南市| 紫云| 西盟| 红桥区| 巴林左旗| 东乌| 陇南市| 古浪县| 大埔区| 龙州县| 凤凰县| 崇明县|