kotlin抽象类、密封类、接口

Java抽象类、密封类与接口详解
本文主要介绍了Java中的抽象类、密封类和接口。抽象类是一种模板模式,不能实例化但可有构造方法;密封类是特殊抽象类,子类固定且须与密封类在同一文件;接口规定了系统与外界交互规范,不能实例化但可声明变量。

1.抽象类

(1)概念

​ 从多个具有相同特征的类中抽象出一个使用abstract修饰的类,以这个抽象类作为其子类模板,从而避免子类设计的随意性。抽象类的设计是一种模板模式的设计模式。

(2)特点

  • 抽象类、抽象属性、抽象方法都不需要open修饰符,open修饰符和abstract不能共存。

  • 抽象类不能实例化,但是可以有构造方法,构造方法是给子类用的。

  • 抽象类可以包含:属性(抽象属性或者非抽象属性)、方法(抽象方法或者非抽象方法)、构造器、初始化块、嵌套类(接口、枚举)5中成员。

  • 含有抽象成员(包括直接定义了一个抽象成员、或者继承了一个抽象父类但是没有完全实现父类包含的抽象成员、或者实现了一个接口但没有完全实现接口包含的抽象成员这三种情况)只能被定义为抽象类。
  • abstract不能修饰局部变量。

(3)抽象成员(抽象方法、抽象属性)

​ 抽象类里面使用abstract修饰的方法和属性。

  • 抽象方法不能有方法体。

  • 抽象属性不需要初始化。

(4)定义抽象类

下面是抽象类的定义方法

/**
 * 定义一个抽象类
 */
abstract class AbstractClass {
    //抽象属性,不能初始化
    abstract var abstractParam : String
    //抽象方法,不能有方法体
    abstract fun abstractFun()
    //普通属性,子类也可以重写,但是需要open修饰
    var commonParam : Int = 1
    //普通方法,子类也可以重写,但是需要open修饰
    fun commonFun() {
        //方法执行体
    }
    constructor() {
        //构造器,也可以在定义类时定义主构造器,抽象类的构造器是给子类用的,抽象类本身不能实例化
    }
}

下面是子类继承于抽象类的示例

fun main(args: Array<String>) {
    val fruit1 = Orange("橙子")
    val fruit2 = Apple()
    fruit1.eat()
    fruit2.eat()
}

/**
 * 水果抽象类
 */
abstract class Fruit {
    fun eat() {
        println("吃水果:$fruitName ,获取的水果重量${getFruitWeight()}")
    }
    abstract val fruitName : String
    abstract fun getFruitWeight() : Int
}

/**
 * 橘子子类
 */
class Orange(override val fruitName: String) : Fruit() {
    override fun getFruitWeight(): Int {
        return 10
    }
}

/**
 * 苹果子类
 */
class Apple : Fruit() {
    override fun getFruitWeight(): Int {
        return 20
    }

    override val fruitName: String
        get() = "苹果"

}

2.密封类(特殊的抽象类)

(1)概念

​ 密封类是一种特殊的抽象类,专门用来派生子类。使用sealed修饰符修饰。

(2)特点

​ 密封类的子类是固定的。

原因如下:

  • 密封类的子类必须与密封类在同一个文件。
  • 在其他文件中不能不能为密封类派生子类(但是密封类的子类可以被其他类继承)。

(3)定义密封类

/**
 * 密封类
 */
sealed class SealedClass {
    abstract fun sealedFun()
}

/**
 * 密封类子类1
 */
class SubClass1 : SealedClass() {
    override fun sealedFun() {
        
    }
}

/**
 * 密封类子类2(使用open修饰,可以在其他文件派生子类)
 */
open class SubClass2 : SealedClass() {
    override fun sealedFun() {
        
    }
}

3.接口

(1)概念

​ 接口定义了系统与外界交互的窗口或者说规范,规定了实现者必须向外界提供哪些服务(方法、属性)。

(2)特点

  • 修饰符:public|internal|private或者省略(省略即是public);
  • 接口只能继承接口,不能继承类;
  • 接口中可以定义抽象/非抽象方法;
  • 接口中可以定义抽象属性/非抽象属性,但是接口中的费抽象属性是没有幕后字段(filed)的,因此需要为之提供get-set方法。
  • kotlin接口中的非抽象成员(方法、属性)可使用public|private两种访问权限修饰(java中自动为接口成员提供public修饰,如果指定访问权限也只能是public),抽象成员只能使用public修饰;
  • 接口不可以包含构造器和初始化块,但接口可包含:方法(抽象方法/非抽象方法)、属性(抽象属性/非抽象属性)、嵌套类(或嵌套接口、嵌套枚举);
  • kotlin接口抽象成员的abstract关键字可以省略(抽象类的抽象成员不能省略);
  • 接口不能实例化,但可以用来声明变量,通过该变量可以赋值给Any(向上转型,因为接口的子类对象一定是Any的子类);
  • 接口的实现类实现该接口的抽象成员只能是public修饰。

(3)定义接口

fun main(args: Array<String>) {
    val person: Human = People("男", 5, 10)
    person.speak()
    (person as? Child)?.howOldAreYou()//向下转型为Child类型
    println("年级:${(person as? Student)?.grade}")//向下转型为Student类型
    val any : Any = person//person可以直接赋给Any
}

/**
 * 定义一个人类接口
 */
interface Human {
    //抽象属性,可省略abstract关键字,var type : String,只能使用public修饰
    abstract var type: String
    //非抽象属性,可以使用public|private访问权限符修饰
    val sex: String
        get() = type

    //非抽象方法,可以使用public|private访问权限符修饰
    fun speak() {
        if (canSpeak()) println("我是${sex}的") else println("不会说话")
    }

    //抽象方法,可省略abstract关键字,fun canSpeak() : Boolean
    abstract fun canSpeak(): Boolean
}

/**
 * 定义一个学生接口
 */
interface Student {
    val grade: Int
}

/**
 * 定义一个小孩抽象类
 */
abstract class Child {
    //抽象类的抽象属性,abstract不能省略
    abstract val age: Int
    //抽象类的非抽象方法(有方法体)
    fun howOldAreYou() {
        println("年龄:$age")
    }
}

/**
 * 定义Person类,继承于Child抽象类,实现了Student接口、Human接口,类后面的抽象类只能有一个,接口可以有多个,摆放顺序随意
 */
class People(override var type: String, override val grade: Int, override val age: Int) : Child(), Student, Human {
    override fun canSpeak(): Boolean {
        return true
    }
}

转载于:https://www.cnblogs.com/nicolas2019/p/10940504.html

### Kotlin 密封类与枚举类的区别 #### 定义与基本概念 在 Kotlin 中,密封类Sealed Classes)和枚举类(Enum Classes)都是用于表示有限集合的工具。然而,两者的设计目标和适用场景存在显著差异。 - **枚举类**是一种特殊的类形式,它允许定义一组固定的常量值。这些常量值是该类的唯一实例[^2]。 - **密封类**则是一个抽象的概念容器,它可以拥有多个具体的子类,但这些子类必须在同一文件中声明。通过这种方式,密封类能够表达更复杂的层次结构并支持多种不同的实现方式[^1]。 --- #### 成员属性与行为能力 - 枚举类中的每一个成员本质上只是一个对象实例,并且无法携带额外的状态或复杂的行为逻辑。如果需要扩展其功能,则可以通过 `object` 的方式为其单独添加方法或者字段[^2]。 - 而对于密封类来说,它的各个具体子类不仅可以继承父类的功能,还可以进一步自定义自己的状态参数以及独特的方法实现。这种灵活性使得密封类更适合用来描述那些具有不同表现形态的对象群组[^2]。 ```kotlin // 枚举类示例 enum class Color { RED, GREEN, BLUE; fun description(): String = when (this) { RED -> "Red color" GREEN -> "Green color" BLUE -> "Blue color" } } // 密封类示例 sealed class Result { data class Success(val value: Int) : Result() object Failure : Result() override fun toString(): String = when (this) { is Success -> "Success with $value" is Failure -> "Operation failed" } } ``` --- #### 使用范围与典型应用场景 - 当只需要简单地列举一些选项而不涉及任何动态变化的数据时,应该优先考虑使用枚举类。比如颜色的选择、星期几等等这样的情况非常适合作为枚举项来管理。 - 如果面临的是更加灵活的需求——即不仅限于静态列表而且还需要根据不同条件做出相应调整的话,则应当选用密封类作为解决方案之一[^1]。例如解析网络请求的结果可能成功也可能失败;又或者是游戏开发过程中角色的动作类型既包含了基础动作也涵盖了高级技能释放等多种可能性等情形下都可以借助密封类构建清晰易懂的业务模型[^1]。 --- #### 总结比较表 | 特性/类别 | 枚举类 | 密封类 | |------------------|-------------------------------------|---------------------------------------| | 实例数量 | 只能有一个单一实例 | 子类可有多个独立实例 | | 数据承载能力 | 不具备数据存储 | 支持携带任意数量及种类的数据 | | 方法定义权限 | 所有成员共享同一套公共API | 各个子类均可自由定制专属操作接口 | | 文件位置约束 | 无特别限制 | 必须与其直接派生的所有子类位于同一个源码单元内 | --- ### 示例代码展示 以下是两个实际应用的例子分别展示了如何利用这两种机制解决问题: ```kotlin fun processColor(color: Color): Unit = println(color.description()) val colorsList = listOf(Color.RED, Color.GREEN, Color.BLUE) colorsList.forEach { c -> processColor(c) } ``` 上面这段程序片段演示了怎样基于预设好的几种色彩来进行统一处理流程调用。 相比之下下面这个例子则是关于采用密封类解决更为棘手状况下的策略模式设计思路: ```kotlin fun handleResult(result: Result): String = result.toString() val resultsSequence = sequenceOf(Result.Success(42), Result.Failure) resultsSequence.map(::handleResult).forEach(::println) ``` 在这里我们看到即使面对不确定性的输入也能依靠精心规划过的分类体系得到妥善处置结果反馈给用户端呈现出来。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值