Kotlin特殊类

思维导图数据类Data Class 

数据类即只用来保存数据的类

数据类会根据主构造函数的定义的属性自动推导出equals、hashcode、toString、componentN以及copy函数

数据类的声明

数据类采用 data class 声明,必须有主构造函数且采用定义属性的方式,主构造函数至少定义一个属性

数据类不可继承open、不可内部inner、不可密封sealed、不可抽象abstract

data class CarBean(var name:String,var sex:String,var age:Int){}
fun main(args:Array<String>){
    //CarBean 默认实现toString
    val bean1=CarBean("Peter","Male",17) 
    val bean2=CarBean("Alice","Female",18) 
    println(bean1)//CarBean(name=Peter, sex=Male, age=17)
    println(bean2)//CarBean(name=Alice, sex=Female, age=18)
    //默认实现equals 比较主构造参数内的所有定义属性
    println(bean1==CarBean("Peter","Male",17))//true
    //默认实现hashcode
    println(bean1.hashCode())//1061749360
    println(CarBean("Peter","Male",17).hashCode())//1061749360
    //默认实现copy
    val bean3=bean1.copy(age=18)//更改年龄,其他相同
    println(bean3)//CarBean(name=Peter, sex=Male, age=18)
    //默认实现componentN
    val (name,sex,age)=bean3
    println("$name $sex $age")//Peter Male 18
}

注意事项

1.如果数据类显式实现了toString或者equals或者hashcode函数,则数据来使用这些方法时,使用显式实现

2.数据类不能提供copy和componentN函数的显式实现

特点

1.使用自动生成的copy函数可以进行已有对象的拷贝,且()内可以使用关键字传参指定与已有对象不同的属性

语法:val/var 变量=对象.copy(属性=值),默认是完全拷贝

2.使用自动生成的componentN函数可以对数据类对象进行解构

语法:val/var (变量 数目与属性数形态)=对象

在类体中声明的属性

因为数据类的toString、equals、hashcode、copy、componentN函数均是根据主构造函数内定义的属性生成的,并不包括类体中属性初始化器声明的属性,所以调用这些方法时是不针对类体中的属性的,如果需要针对则可用重写toString、equals、hashcode函数‘

data class CarBean(var name:String,var sex:String){
    var age:Int=0
}
fun main(args:Array<String>){
    val bean1=CarBean("Peter","Male")
    val bean2=CarBean("Alice","Female")
    println(bean1)//CarBean(name=Peter, sex=Male)
    println(bean2)//CarBean(name=Alice, sex=Female)
    bean1.age=18
    println(bean1==CarBean("Peter","Male"))//True
    println(bean1.hashCode().toString()+" "+CarBean("Peter","Male").hashCode())
    //-1905412671 -1905412671
    //println(bean1.copy(age=18)) //Cannot find a parameter with this name: age
    println(bean1.copy(name="Job")) //只能拷贝主构造函数中有的属性 CarBean(name=Job, sex=Male)
    val (name,sex)=bean1 //也只能解析主构造函数中的属性
}

当类中重写了toString、hashcode、equals方法时,则将会对除copy和解析之外的使用产生影响

data class CarBean(var name:String,var sex:String){
    var age:Int=0
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as CarBean

        if (name != other.name) return false
        if (sex != other.sex) return false
        if (age != other.age) return false

        return true
    }

    override fun hashCode(): Int {
        var result = name.hashCode()
        result = 31 * result + sex.hashCode()
        result = 31 * result + age
        return result
    }

    override fun toString(): String {
        return "CarBean(name='$name', sex='$sex', age=$age)"
    }
}
fun main(args:Array<String>){
    val bean1=CarBean("Peter","Male")
    val bean2=CarBean("Alice","Female")
    println(bean1)//CarBean(name='Peter', sex='Male', age=0)
    println(bean2)//CarBean(name='Alice', sex='Female', age=0)
    bean1.age=18
    println(bean1==CarBean("Peter","Male"))//False
    println(bean1.hashCode().toString()+" "+CarBean("Peter","Male").hashCode())
    //1061749361 1061749343
    //println(bean1.copy(age=18)) //Cannot find a parameter with this name: age
    println(bean1.copy(name="Job")) //只能拷贝主构造函数中有的属性 CarBean(name=Job, sex=Male, age=0)
    val (name,sex)=bean1 //也只能解析主构造函数中的属性
}

密封类

什么是密封类

密封类是用来表示受限的类继承结构:对象只能表示有限的几种类型

密封类和枚举类类似,枚举类是表示受限对象的类,类只能有这几个实例对象,一个是针对的变量的类型,一个是针对变量的值

密封类的声明使用

声明一个密封类,需要在类名前面添加 sealed 修饰符

密封类的子类必须与密封类在同一文件中,而密封类的子类的子类无此限制

密封类不允许有非private的构造函数,所有密封类不能直接实例化

密封类的子类需有状态,且需要重写equals函数

一般使用是在when表达式中,验证密封类对象的类型

sealed class A{}
data class B(val name:String):A(){}
data class C(val sex:String):A(){}
object D:A()
fun main(args:Array<String>){
    val a:(x:A)->Unit={
        when(it){
            D->{
                println("$it is D")
            }
            is B->{
                println("$it is B")
            }
            is C->{
                println("$it is C")
            }
        }
    }
    //包含了所有的情况,when内不需要else
}

嵌套类

类简单的嵌套在其他类中相当于C++中的静态内部类,不能使用外部类的成员方法

类可用和接口互相嵌套,接口也可以嵌套接口

类内嵌套接口,是静态嵌套的(接口不能使用inner关键字

类内嵌套类,一般是静态嵌套,如果有inner关键字则是类的成员

接口内嵌套类或者接口均是静态嵌套(接口内也不能用inner关键字

class A(val nameA:String){
    fun printA(){
        print("$nameA ")
        println("This is method of A ,which is named printA")
    }
    //类内嵌套类
    class B(val nameB:String){
        //printA() Expecting member declaration
        fun printB(){
            //name Unresolved reference: name
            print("$nameB ")
            println("This is method of B ,which is named printB")
        }
    }
    //嵌套类相当于C++静态内部类,不能使用外部类的成员

    //类内嵌套接口
    fun interface C{
        fun write()
    }
}
interface B{
    class A{
        fun work(){

        }
    }
    //接口内嵌套类是静态嵌套的,类不是默认抽象的
    interface C{
        fun work()
    }
    //接口内嵌套接口也是静态嵌套的,继承B,并不需要实现C
}

fun main(args:Array<String>){
    val a=A("Alice") 
    val b=A.B("Bob") //可以直接使用外部类名访问嵌套类
    a.printA() //Alice This is method of A ,which is named printA
    b.printB() //Bob This is method of B ,which is named printB
    val c=A.C{ println("This is A.C interface achievement") }
    c.write()// This is A.C interface achievement
}

内部类

内部类是特殊的嵌套类,即在嵌套类声明定义前加inner关键字,此时的嵌套类叫做内部类

内部类是外部类的一个成员,需要凭借对象才能访问,内部类也可以访问外部类的其他成员

class A(val nameA:String){
    fun printA(){
        print("$nameA ")
        println("This is method of A ,which is named printA")
    }
    //类内嵌套类
    inner class B(val nameB:String){
        fun printB(){
            print("$nameB $nameA")
            println("This is method of B ,which is named printB")
            printA()
        }
        //内部类可以使用外部类的所有成员(private public internal protected)
    }
}

fun main(args:Array<String>){
    val a=A("Alice")
    //val b=A.B("Bob") //不能直接用外部类名访问内部类
    val b=a.B("Bob") //需要通过外部类的对象访问内部类
    a.printA() //Alice This is method of A ,which is named printA
    b.printB() 
    //Bob AliceThis is method of B ,which is named printB
    //Alice This is method of A ,which is named printA
}

匿名内部类

可用使用 object:类的对象{ } 来创建匿名内部类

当对象是函数式接口时,可用直接使用 接口名{函数体} 来创建

    window.addWindowFocusListener(object :WindowFocusListener{
        override fun windowGainedFocus(e: WindowEvent?) {
            TODO("Not yet implemented")
        }
        override fun windowLostFocus(e: WindowEvent?) {
            TODO("Not yet implemented")
        }
    })

这个在后续Android开发中添加按钮比较常用

枚举类 

枚举类是对对象的值有限制,对象只能是枚举常量

声明格式 enum class 名称{枚举常量} 

定义枚举常量时可以根据主构造函数参数设置属性定义

定义枚举常量时可以实现枚举类的抽象方法

枚举常量定义使用与其他类成员分隔

enum class MySize(){
    Small,Middle,Max,SuperMax
}//普通定义

enum class MyColor(var rgb:Int){
    Red(0),
    Black(1),
    Blue(2),
    Pink(3)
}//定义枚举常量时设置对象的属性值

enum class MyAddress{
    BeiJing{
        override fun address() {
            println("This is BeiJing")
        }//每个枚举常量军均需实现枚举类的抽象方法
    },
    ShangHai{
        override fun address() {
            println("This is ShangHai")
        }//每个枚举常量军均需实现枚举类的抽象方法
    },
    QingDao{
        override fun address() {
            println("This is QingDao")
        }//每个枚举常量军均需实现枚举类的抽象方法
    }; //使用 ; 隔离枚举常量与其他类成员
    abstract fun address()
}

fun main(args:Array<String>){
    val color= MyColor.Red
    println(color.rgb) //0
    val size=MySize.Max
    println(size) //Max
    val address=MyAddress.QingDao
    address.address() //This is QingDao
}

枚举类实现接口 

枚举类实现接口有两种方式

1.由枚举类提供统一的实现方式

2.每个枚举常量均对接口进行实现

interface A{
    fun doPerWork()
    fun allWork()
}

enum class Color:A{
    Red{
        override fun doPerWork() {
            println("This is Red")
        }
       },
    Blue{
        override fun doPerWork() {
            println("This is Blue")
        }
        },
    Black{
        override fun doPerWork() {
            println("This is Black")
        }
    };

    override fun allWork() {
        println("This is Color Class")
    }
}

fun main(args:Array<String>){
    val red=Color.Red
    red.allWork() //This is Color Class
    red.doPerWork() //This is Red
}

内联类

和内联函数类似,内联函数使得在JVM中高阶函数其中的函数参数并不以函数调用的形式呈现在高阶函数体中,而是直接在高阶函数体内执行所调用的函数体,减少运行内存损耗

内联类的特点

内联类则是对主构造函数中的唯一的一个属性的内联使用内联类的其他成员相当于直接使用静态成员(相当于定义在顶层中

语法:inline class 类名(val/var 变量名:类型){ }

在IDEA中定义inline class时会有修改提示

替换后即是

@JvmInline
value class Test(val name:String){

}
@JvmInline
value class Test(val name:String){
    val age: Int
        get() {
            return name.length
        }
    fun doWork(){
        println("$name ")
    }
}
fun main(args:Array<String>){
    val test=Test("Bob")
    println(test) //Test(name=Bob)
    test.doWork() //Bob 
}

注意事项

  • 内联类不能含有 init 代码块
  • 内联类不能含有幕后字段field
  • 当内联类对象传参时,内联类类型与参数类型相同时为拆箱,不同时是装箱
//文档中的代码示例
interface I

inline class Foo(val i: Int) : I

fun asInline(f: Foo) {}
fun <T> asGeneric(x: T) {}
fun asInterface(i: I) {}
fun asNullable(i: Foo?) {}

fun <T> id(x: T): T = x

fun main() {
    val f = Foo(42) 
    
    asInline(f)    // 拆箱操作: 用作 Foo 本身
    asGeneric(f)   // 装箱操作: 用作泛型类型 T
    asInterface(f) // 装箱操作: 用作类型 I
    asNullable(f)  // 装箱操作: 用作不同于 Foo 的可空类型 Foo?
    
    // 在下面这里例子中,'f' 首先会被装箱(当它作为参数传递给 'id' 函数时)然后又被拆箱(当它从'id'函数中被返回时)
    // 最后, 'c' 中就包含了被拆箱后的内部表达(也就是 '42'), 和 'f' 一样
    val c = id(f)  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值