Kotlin学习历程七:kotlin中的类之密封类、抽象类和内部类(嵌套类)

一、密封类 sealed

1.1 受限的类继承结构

所谓受限的类继承结构,即当类中的一个值只能是有限的几种类型,而不能是其他的任何类型。

这种受限的类继承结构从某种意义上讲,它相当于是枚举类的扩展。但是,我们知道Kotlin的枚举类中的枚举常量是受限的,因为每一个枚举常量只能存在一个实例。

但是其和枚举类不同的地方在于,密封类的一个子类可以有可包含状态的多个实例。

也可以说成,密封类是包含了一组受限的类集合,因为里面的类都是继承自这个密封类的。但是其和其他继承类(open)的区别在,密封类可以不被此文件外被继承,有效保护代码。但是,其密封类的子类的扩展是是可以在程序中任何位置的,即可以不再统一文件下。

1.2 声明

关键字:sealed

声明格式:

sealed class SealedExpr()

注意:密封类是不能被实例化的

1.3 作用及用法

1.3.1 作用

用来表示受限的类继承结构

sealed class SealedExpr{
data class Person(val num1 : Int, val num2 : Int) : SealedExpr()

object Add : SealedExpr()   // 单例模式
object Minus : SealedExpr() // 单例模式
}

// 其子类可以定在密封类外部,但是必须在同一文件中 v1.1之前只能定义在密封类内部
object NotANumber : SealedExpr() 

分析:即所定义的子类都必须继承于密封类,表示一组受限的类

1.3.2 和普通继承类的区别

普通继承来使用open关键字定义,在项目中的类都可以继承自该类;
而密封类的子类必须在密封类内部或者密封类的同一文件内,即有效的代码保护。

1.3.3 和枚举类的区别

枚举类中的每一个枚举常量都只能存在一个实例;
而密封类的子类可以存在多个实例。

1.3.4 其子类的类扩展实例

在kotlin支持扩展功能,其和C#、Go语言类似。这一点是java所不具备的。

sealed class SealedExpr{
    data class Person(val name : String, val age : Int) : SealedExpr()
    object Add : SealedExpr()
    companion object Minus : SealedExpr()
}

object NotANumber : SealedExpr()

其存在TestSealedDemo.kt文件中

fun  <T>SealedExpr.Add.add(num1 : T, num2 : T) : Int{
    return 100
}

fun main(args: Array<String>) {
    println(SealedExpr.Add.add(1,2))
}

输出结果为:
100

1.3.5 使用密封类的好处

  • 有效的代码保护
  • 在使用when表达式的时候,如果能够验证语句覆盖了所有的情况,就可以不再为该语句添加else子句了。
package hello    

sealed class SealedExpr{
    data class Person(val name : String, val age : Int) : SealedExpr()
    object Add : SealedExpr()
    companion object Minus : SealedExpr()
}

object NotANumber : SealedExpr()

fun eval(expr: SealedExpr) = when(expr){
    is SealedExpr.Add -> println("is Add")
    is SealedExpr.Minus -> println("is Minus")
    is SealedExpr.Person -> println(SealedExpr.Person("Koltin",22))
    NotANumber -> TODO()
}

fun main(args: Array<String>) {
    val mPerson1 = SealedExpr.Person("name1",22)
    println(mPerson1)

    val mPerson2 = SealedExpr.Person("name2",23)
    println(mPerson2)

    println(mPerson1.hashCode())
    println(mPerson2.hashCode())

    eval(SealedExpr)
}

输出结果:
Person(name=name1, age=22)
Person(name=name2, age=23)
-1052833328
-1052833296
is Minus

二、抽象类 abstract

在实际可发过程中,一般都会写一个基类,封装常用的方法、以及处理一些共有的逻辑 ,但是程序逻辑是根据每个类不同的功能去实现不同的代码。而这个所谓的基类,一般都是一个抽象类。不管是java还是kotlin,实现抽象类的作用就在于此。

抽象类可以理解为类定义一个模板,所有的子类都根据这个模板去填充自己的代码。

2.1 声明

关键字:abstract

抽象可以分为抽象类、抽象函数和抽象属性。

抽象类和普通类的区别在于:抽象类除了可以有其自己的属性、构造函数、方法等组成部分外,还包含了抽象函数以及抽象属性

abstract class Lanauage{
    val TAG = this.javaClass.simpleName  // 自身的属性
    
    // 自身的函数
    fun test() : Unit{
        // exp
    }
    abstract var name : String           // 抽象属性
    abstract fun init()                  // 抽象方法
}

/**
 * 抽象类Lanauage的实现类TestAbstarctA
 */
class TestAbstarctA : Lanauage(){

    override var name: String
        get() = "Kotlin"
        set(value) {}

    override fun init() {
        println("我是$name")
    }
}

/**
 * 抽象类Lanauage的实现类TestAbstarctB
 */
class TestAbstarctB : Lanauage(){
    override var name: String
        get() = "Java"
        set(value) {}

    override fun init() {
        println("我是$name")
    }
}

fun main(args: Array<String>) {
    
    // val lanauage = Lanauage() 是错误的,因为抽象类不能直接被实例化
    
    val mTestAbstarctA = TestAbstarctA()
    val mTestAbstarctB = TestAbstarctB()

    println(mTestAbstarctA.name)
    mTestAbstarctA.init()

    println(mTestAbstarctB.name)
    mTestAbstarctB.init()
}

输出结果:
Kotlin
我是Kotlin
Java
我是Java

抽象类本身具有普通类特性及其组成部分,不过,抽象类不能直接被实例化;
抽象类的子类必须全部重写带abstract修饰的属性和方法;
抽象成员只有定义,没有实现,都用abstract修饰符修饰;
抽象类是为其子类定义了一个模板,不同的类实现不同的功能。

2.2 规则

  • 在kotlin中的抽象类在顶层定义的时候只能使用public可见性修饰符修饰;
  • 抽象类中可以定义内部抽象类;
  • 只能继承一个抽象类;
  • 若要实现抽象类的实例化,需要依靠子类采用向上转型的方式处理;
  • 抽象类可以继承自一个继承类,即抽象类可以作为子类。不过,抽象类建议不用open修饰,因为可以覆写抽象类的父类的函数。

2.3 实际应用

在Java的设计模式中,有一种设计模式叫模板设计模式,其定义为:

  • 定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。
  • 通俗点的理解就是 :完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。每个步骤的具体实现,由子类完成。

Kotlin和Java是互通的,说明Kotlin也是支持这种设计模式的。

如果你对Java中的模板设计模式不是很了解的,请参见这篇文章

三、内部类(嵌套类)

在实际开发中,用到内部类的地方是很多的。比如说:

对于Android开发来说,列表适配器(adapter)中的ViewHolder类,就是一个内部类。
根据后台开发人员提供的json字符串生成的对象中,也包含另外一个对象,这也是一个内部类。

3.1 嵌套类

即:一个类可以嵌套在其他类中。

class Other{           // 外部类
    val numOuther = 1

    class Nested {      // 嵌套类
        fun init(){
            println("执行了init方法")
        }
    }
}

fun main(args: Array<String>) {
    Other.Nested().init()      // 调用格式为:外部类.嵌套类().嵌套类方法/属性
}

输出结果为:
执行了init方法

注意:

  • 调用嵌套类的属性或方法的格式为:外部类.嵌套类().嵌套类方法/属性。在调用的时候嵌套类是需要实例化的
  • 嵌套类不能使用外部类的属性和成员

3.2 内部类

在上面的例子中讲解了嵌套类的使用,而内部类和嵌套类还是有一定的区别的,而且内部类是有特定的关键字去声明的。

1.声明

关键字:inner

声明格式:

inner class 类名(参数){}

class Other{            // 外部类
    val numOther = 1

    inner class InnerClass{     // 嵌套内部类
        val name = "InnerClass"
        fun init(){
            println("我是内部类")
        }
    }
}

fun main(args: Array<String>) {
   Other().InnerClass().init()  // 调用格式为:外部类().内部类().内部类方法/属性
}

注意:

  • 调用内部类的属性或方法的格式为:外部类().内部类().内部类方法/属性。在调用的时候嵌套类是需要实例化的
  • 内部类不能使用外部类的属性和成员

2.匿名内部类

作为一名Android开发者,对匿名内部类都不陌生,因为在开发中,匿名内部类随处可见。比如说Button的OnClickListener,ListView的单击、长按事件等都用到了匿名内部类。
一般的使用方式为定义一个接口,在接口中定义一个方法。

class Other{
    
    lateinit private var listener : OnClickListener

    fun setOnClickListener(listener: OnClickListener){
        this.listener = listener
    }
    
    fun testListener(){
        listener.onItemClick("我是匿名内部类的测试方法")
    }
}    

interface OnClickListener{
    fun onItemClick(str : String)
}

fun main(args: Array<String>){
    // 测试匿名内部类
    val other = Other()
    other.setOnClickListener(object : OnClickListener{
        override fun onItemClick(str: String) {
            // todo
            println(str)
        }
    })
    other.testListener()
}

输出结果为:
我是匿名内部类的测试方法

3.3 局部类

所谓局部类,这一点和Java是一致的。即定义在方法(函数)中的类。

class Other{    // 外部类
    val numOther = 1
    
    fun partMethod(){
        var name : String = "partMethod"

        class Part{
            var numPart : Int = 2

            fun test(){
                name = "test"
                numPart = 5
                println("我是局部类中的方法")
            }
        }

        val part = Part()
        println("name = $name \t numPart = " + part.numPart + "\t numOther = numOther")
        part.test()
        println("name = $name \t numPart = " + part.numPart + "\t numOther = numOther")
    }
}

fun main(args: Array<String>) {
    // 测试局部类
    Other().partMethod()
}

输出结果为:
name = partMethod    numPart = 2    numOther = 1
我是局部类中的方法
name = test      numPart = 5    numOther = 1

注意:

  • 局部类只能在定义该局部类的方法中使用。
  • 定义在实例方法中的局部类可以访问外部类的所有变量和方法。但不能修改
  • 局部类中的可以定义属性、方法。并且可以修改局部方法中的变量。

3.4 静态类

熟悉Java的朋友都知道Java的静态类,或者说用static修饰符修饰的类。但是在Kotlin中,是不存在static关键字的。

1.静态类

Kotlin语言中使用"object"修饰静态类,被修饰的类,可以使用类名.方法名的形式调用

object Util {

    fun getCurrentVersion(): String {
        return BuildConfig.VERSION_NAME
    }

}

调用:
var version_name1 = Util.getCurrentVersion()

2.静态方法

Kotlin语言中使用"companion object"修饰静态方法,可以使用类名.方法名的形式调用

class Util2 {

    companion object {
        fun getCurrentVersion(): String {
            return BuildConfig.VERSION_NAME
        }
    }

}

调用:
var version_name2 = Util2.getCurrentVersion()

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin嵌套类(Nested Classes)和内部类(Inner Classes)是非常相似的概念,但有一些细微的区别。以下是对它们的一些基本介绍: 1. **嵌套类(Nested Classes)**:在Kotlin嵌套类是一种类,它包含在另一个类的内部。它可以是静态的(static)、非静态的(inner)或者局部的(local)。嵌套类可以从任何访问它的外部类访问字段、方法、构造器和嵌套类。这允许一个类的数据可以在嵌套被更细粒度地访问和控制。 ```kotlin class OuterClass { class NestedClass { // code inside } } ``` 2. **内部类(Inner Classes)**:内部类是定义在另一个类内部的类。它可以直接访问外部类的所有成员,包括字段、方法、构造器和嵌套类。内部类可以包含自己的构造函数,并且可以访问外部类的私有成员。内部类可以嵌套在其他内部,形成一个复杂的层次结构。 ```kotlin class OuterClass { inner class InnerClass { // code inside } } ``` 总的来说,内部类在实现接口、继承某个类或封装内部数据结构时特别有用。另一方面,嵌套类提供了一种更灵活的方式来访问外部类的数据和方法,即使外部类在不同的包或模块。 需要注意的是,尽管Kotlin嵌套类和内部类非常相似,但在某些情况下,使用嵌套类的语法可能更清晰和直观。例如,使用嵌套类可以更清楚地表示一个类的嵌套结构,而内部类则更适合实现接口或封装内部数据结构。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值