在面向对象编程(OOP)之前,编码要复杂得多,因为像C这样的语言不允许你在高层次上抽象概念。抽象通过将共同特征组合在一起来创建对象来帮助您构建项目。例如,毛皮颜色,尾巴长度和年龄是制作猫的特征。
从本质上讲,抽象通过提供定义真实世界对象的工具使编码更简单。它还具有其他好处,例如使代码更易于阅读,理解和维护。但是,它也可能使您的生活复杂化。
在本教程中,您将探索抽象类和接口之间的差异,并了解何时使用它们。您将创建动物园指南,这是一个显示不同动物信息的应用程序。在此过程中,您将了解到:
1、关于继承。
2、如何实现抽象类和接口。
3、抽象类和接口之间的差异。
4、创建一个利用抽象类、接口和继承的应用程序。
我们先看一个应用程序,可以看到它有两个屏幕:
主屏幕显示您可以单击的动物列表。
第二个屏幕显示有关您选择的动物的一些详细信息。
项目的结构如下:
![](https://img.haomeiwen.com/i27820230/c124c865a39c178a.png)
该项目包括六种动物:蝙蝠,大象,长颈鹿,狮子,鹦鹉和企鹅。还有两个主要类别:鸟类和哺乳动物。
在 Kotlin 中,默认情况下有一个类及其成员。因此,如果要扩展它们,则必须添加关键字
看看动物,哺乳动物和狮子类,看看继承如何运作的演示:
// 1
open class Animal(
open val name: String,
@DrawableRes open val image: Int,
open val food: Food,
open val habitat: Habitat
) {
open fun getHungerAmount() : Int {
return 0
}
//...
}
// 2
open class Mammal(
override val name: String,
@DrawableRes override val image: Int,
override val food: Food,
override val habitat: Habitat,
val furColor: List<Int>
) : Animal(name, image, food, habitat)
// 3
class Lion : Mammal(
"Lion",
R.drawable.animal_elephant_sample,
Food.ZEBRAS,
Habitat.SAVANNA,
listOf(Color.parseColor("#CB9C70"))
) {
override fun getHungerAmount() : Int {
return (0..100).random()
}
}
这就是继承的原则。它是一个OOP概念,也是在类之间建立关系的基本方法。此方法有三个主要问题:
1、由于动物和哺乳动物是,你可以创建它们的实例,这没有任何意义,因为它们只是抽象的概念,而不是具体的,就像狮子一样。这种不规则性是极其危险的。
2、您需要子类来实现父类中的方法和属性。在这种情况下,可以使用构造函数强制子类实现父类中的属性,但不能对方法执行相同的操作。
3、有些哺乳动物具有某些特征,但其他哺乳动物则没有,类不能支持此类行为。
抽象类
您可以将抽象类视为接口和常规类的混合体。抽象类可以具有接口具有的所有内容以及属性和构造函数。因此,可以在抽象类中正确保存状态,但不能实例化抽象类。
抽象类为所有子类提供通用行为。使用抽象类可以创建许多类共有的一组特征。
抽象类可以同时具有常规方法或属性,也可以具有抽象方法或属性。常规方法和属性可以相应地具体主体和值。
相比之下,被定义为抽象的东西还没有一个定义的值。就方法而言,这意味着它没有主体。
每当类使用抽象成员扩展抽象类时,该类必须实现所有抽象成员。当子类必须实现一个方法来创建特定行为时,这是必不可少的。
看看这个例子:
// 1
abstract class Wrestler {
abstract fun themeMusic(): String
}
// 2
class JohnCena : Wrestler() {
override fun themeMusic() = "johncena_theme.mp3"
}
与接口不同,抽象类可以有一个构造函数,从而有一个块。这两种方法对于在创建对象时初始化某些内容都是必不可少的。
实现动物抽象类
从模型/动物包中打开动物。使类抽象:
abstract class Animal
删除现有方法,并将属性作为抽象属性从构造函数移动到类的主体中。
abstract fun getHungerAmount() : Int
abstract val name: String
@get:DrawableRes
abstract val image: Int
abstract val food: Food
abstract val habitat: Habitat
对哺乳动物执行相同的操作,这次添加为不可变的抽象属性:
abstract class Mammal : Animal() {
abstract val furColor: List<Int>
}
对于 Bird,请改为添加一个抽象属性:feathersColor
abstract class Bird: Animal() {
abstract val feathersColor: List<Int>
}
对于 Bat,这是更新后代码的外观:
class Bat : Mammal() {
override val furColor: List<Int>
get() = listOf(
Color.parseColor("#463D4A")
)
override fun getHungerAmount() : Int {
return (0..100).random()
}
override val name: String
get() = "Bat"
override val image: Int
get() = R.drawable.bat
override val food: Food
get() = Food.INSECTS
override val habitat: Habitat
get() = Habitat.CAVE
}
接口
与抽象类不同,接口定义特定行为而不是模板。从现在开始,我经常将接口称为一种行为,因为接口可以连接原本没有任何共同点的对象。
接口的特性
每次你的类实现一个接口时,它都在签署一个契约。该合约说,一旦类实现了接口,它还必须实现接口内定义的所有成员。但是,您将在下一节中探讨一个例外情况。
由于 Kotlin 不支持多重继承,因此编译器不会让一个子类具有多个父类。它只能扩展一个类。但有一个好消息:您可以根据需要实现任意数量的接口。
接口只能定义开放成员。因此,实现接口的每个类都可以访问每个非私有成员。请记住,抽象方法不能是私有的。
接口应用示例
想象一下,你有两个类,它们只有一个共同点:它们都需要电才能工作。你会做些什么来连接它们?请查看下面的代码,了解如何执行此操作的示例:
// 1
interface Pluggable {
val neededWattToWork: Int
//Measured in Watt
fun electricityConsumed(wattLimit: Int) : Int
fun turnOff()
fun turnOn()
}
// 2
class Microwave : Pluggable {
override val neededWattToWork = 15
override fun electricityConsumed(wattLimit: Int): Int {
return if (neededWattToWork > wattLimit) {
turnOff()
0
} else {
turnOn()
neededWattToWork
}
}
override fun turnOff() {
println("Turning off..")
}
override fun turnOn() {
println("Turning on..")
}
}
// 3
class WashingMachine : Pluggable {
override val neededWattToWork = 60
override fun electricityConsumed(wattLimit: Int): Int {
return if (neededWattToWork > wattLimit) {
turnOff()
0
} else {
turnOn()
neededWattToWork
}
}
override fun turnOff() {
println("Turning off..")
}
override fun turnOn() {
println("Turning on..")
}
}
解决此问题的最佳方法是实现定义常见行为的接口,这正是它的作用。现在,扩展此接口的每个类都需要实现所有成员。这样,它们就可以连接到电源。
在 Java 中,只能在接口内定义常量属性。因此,每次尝试在 Kotlin 中添加属性时,如下所示:
String password = "Password1234";
编译器在引擎盖下添加。在Java中,你必须初始化属性,这样你才能做这样的事情:
String password; //This code won't run
另一方面,Kotlin 允许您在接口内定义只能是非静态属性。当您只处理一个值并且方法太多时,这将变得很有用。定义一个方法只是为了返回一个值是不方便的,即使正如反编译的Java代码所显示的那样,编译器无论如何都会为该属性创建一个方法。
public interface Pluggable {
int getNeededWattToWork();
int electricityConsumed(int var1);
void turnOff();
void turnOn();
}
但是,Kotlin 不允许您创建属性,因为属性包含三个组件,这里的问题是接口不能存储值,因为它不能有属性。
网友评论