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

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)
主站蜘蛛池模板: 贵阳市| 米泉市| 广安市| 崇礼县| 礼泉县| 虎林市| 夹江县| 班玛县| 谢通门县| 赤峰市| 绍兴县| 阳泉市| 阳曲县| 杭锦后旗| 赤壁市| 万载县| 呼伦贝尔市| 射洪县| 鹤山市| 新野县| 深泽县| 临清市| 七台河市| 容城县| 邯郸市| 蒙城县| 昭通市| 平阴县| 兴义市| 斗六市| 哈密市| 陇川县| 丰顺县| 日喀则市| 驻马店市| 城口县| 安乡县| 嘉荫县| 铁力市| 天门市| 突泉县|