美文网首页
Kotlin 中的接口和抽象类

Kotlin 中的接口和抽象类

作者: 安安_660c | 来源:发表于2022-10-14 15:29 被阅读0次

    在面向对象编程(OOP)之前,编码要复杂得多,因为像C这样的语言不允许你在高层次上抽象概念。抽象通过将共同特征组合在一起来创建对象来帮助您构建项目。例如,毛皮颜色,尾巴长度和年龄是制作猫的特征。

    从本质上讲,抽象通过提供定义真实世界对象的工具使编码更简单。它还具有其他好处,例如使代码更易于阅读,理解和维护。但是,它也可能使您的生活复杂化。

    在本教程中,您将探索抽象类和接口之间的差异,并了解何时使用它们。您将创建动物园指南,这是一个显示不同动物信息的应用程序。在此过程中,您将了解到:
    1、关于继承。
    2、如何实现抽象类和接口。
    3、抽象类和接口之间的差异。
    4、创建一个利用抽象类、接口和继承的应用程序。

    我们先看一个应用程序,可以看到它有两个屏幕:
    主屏幕显示您可以单击的动物列表。
    第二个屏幕显示有关您选择的动物的一些详细信息。
    项目的结构如下:



    该项目包括六种动物:蝙蝠,大象,长颈鹿,狮子,鹦鹉和企鹅。还有两个主要类别:鸟类和哺乳动物。
    在 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 不允许您创建属性,因为属性包含三个组件,这里的问题是接口不能存储值,因为它不能有属性。

    相关文章

      网友评论

          本文标题:Kotlin 中的接口和抽象类

          本文链接:https://www.haomeiwen.com/subject/hkkczrtx.html