数据类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) }