Kotlin中那些特别的类
类(Class)是面向对象程序设计(OOP,Object0Oriented Programming)实现信息封装的基础。包含属性和方法……
以上是摘抄自百度百科
的关于类描述,作为Android
开发者,我们接触最多的无过于Java
以及现在火热的Kotlin
。
不同于Java
中相对中规中矩的通用简一的类定义方式,在Kotlin
中有了较多的关键字类定义一些特别的类,比如单例类
、伴生对象
、内部类
、密封类
、数据类
等,对比于Java
我们来分析一下这些特别的类,会不会让你学的特别累
一、简化的数据类
数据类(data class
),用于保存元数据的封装类,Java
中的POJO
(Plain Ordinary Java Object)所有都是继承自Object
,并自然而然的有其toString()
、hashcode()
、equals()
等函数。一般都需要有getter/setter
,复杂的Java Bean
的话,手写getter/setter
实在是挺繁琐的,即使有些快捷框架,也未必能尽如人意。
public class Student {
private String name;
private int age;
private String desc;
public Student() {
//无参构造函数
}
/**
* 多参数构造函数
* @param name
* @param age
* @param desc
*/
public Student(String name, int age, String desc) {
this.name = name;
this.age = age;
this.desc = desc;
}
//getter/setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
//...其他setter getter
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", desc='" + desc + '\'' +
'}';
}
}
而Kotlin
中对应数据类data class
data class Student(val name:String,var age:Int,var desc:String)
查看kotlin转java的代码,就会感觉的kt的甜甜蜜蜜
在AS中Tools--Kotlin--ShowKotlinBytecode
得到Kotlin
字节码,点击Decompile
得到对应的Java
代码形式
数据类的知识点:
和其他Kotlin的定义类一样,都是默认final的;
可以看出
Kotlin
中nullable
可空类型是区别的,String
和String?
不一样。图中可见,反编译后对应Java
写法就是@NotNull
的注解;data class
是专用的数据封装类,它的构造函数至少有一个主构造函数及至少一个入参,主构造函数的入参必须有val/var
的修饰(这么才算做是类的属性而非简单的构造入参);数据类会将构造函数内的参数依顺序生成
component123
之类的,便于读取,自然也根据权限修饰符来确定属性的getter/setter
,val
的是没有setter
的;可通过默认值的形式来实现其他无参构造的需求
data class AA(val name:String="")
-
类中声明的属性,如果
data class
不复写toString()
、equals()
、hashcode()
,那么默认判定对象特征值只是用到构造函数内的属性参数,也就会出现即使类内属性值不一致而认定为相等,记住是相等而不是同一对象。//name是构造属性 data class People(val name:String){ var age:Int = 18//类内属性 } //构造参数只有name,如果一致,即使age不一致 val p1 = People("张三") val p2 = People("张三") p1.age = 20 p2.age = 30 //若没有复写equals、hashcode,就会会判定为两个对象相等, p1== p2 就是true //切记!! 相等的原因是没有override equals和hashcode,但是对象本身并不是同一个, p1===p2 是false
数据类有
copy
函数,不同于Java
中clone
,Kotlin
中的copy
就是便捷的帮你new
了对象并赋值了原有对象的参数(根据你的修改与否)@Test fun testKt(){ val a1 = AA("张三") val a2 = AA("张三") val a1Copy = a1.copy() val a2Copy = a2.copy(name = "李四") a1.age = 19 a2.age = 29 println("a1===a2 ${a1 === a2}") println("a1==a2 ${a1 == a2}") println("a1==aCopy ${a1 == a1Copy}") println("a1===a1Copy ${a1 === a1Copy}") println("a1==a2Copy ${a2 == a2Copy}") println("a1===a2Copy ${a2 === a2Copy}") } //输出结果 a1===a2 false a1==a2 true a1==aCopy true a1===a1Copy false a1==a2Copy false a1===a2Copy false
看一下对应
java
代码,可以看出copy
就是甜甜的语法糖,帮你new
-
数据类的解构声明
可能不好理解这个词,看代码就清晰了
data class AA(val name:String,var age:Int,var desc:String) //定义一个函数,返回AA对象 fun getAA():AA{ return AA("zhangsan",39,"a worker") } //调用处,就可感觉到解构声明的魅力 val aaa = getAA()//普通的方式 val (name,age,desc) = getAAA()//解构声明的方式,便于直接使用某些参数,而不需要aaa.name
系统标准库提供了
Pair
和Triple
数据类,分别是两个参数和三个参数的。-
封装性的数据类,可使用泛型方式定义参数类型
//这样数据类可以使用泛型确定内部参数类型,也有其特定使用场景 data class BBB
(val t:T,var r:R,var q:Q,val s:S)
二、不是甜似的密封类
密封类用以表示受限的类继承结构
快速理解,类似于大号的枚举
,用于特定类型限定。与枚举异同
- 枚举,一种特定类型,可有多个枚举常量。每个枚举值只是一个实例。
- 密封类,可有很多子类,每个子类都可有多个实例。
-
密封类的使用特性注意点
-
sealed
关键字,其所有子类都必须在同一kt
文件内,且最好是top level
的; - 若在其他类内声明,则其子类就只能在自身内部声明了;
- 密封类是抽象的,可有抽象成员,但不能实例化;
- 密封类构造函数私有;
-
-
演示更直白
class ExampleUnitTest { //在其它类内声明,则其子类也就只能在其内部了,根源在于sealed class的私有化构造函数 sealed class HHH() { object mmm : HHH() } object hhhhhhh : HHH()//无法在其它类内继承 密封类 data class eeeeee(val name: String) : AAAA() { class eee(val age: Int) : AAAA() } } //在外也不能继承 其他类内 看似不报错的密封类 object ggg : ExampleUnitTest.HHH() //这才是合规的 sealed class AAAA { fun aa() {} val bbb: String = "" open fun ad() {} abstract class ccc() {} //在它自身内部可以 object jjj : AAAA() } data class ccc(val name: String) : AAAA() { } class ddd(val age: Int) : AAAA() object fff : AAAA()
三、Object
类
Object
类可以快速创建Kotlin
版本的单例类,也可以是companion object
的伴生对象,其特性差不多。
object
单例类,也是kotlin
的类的一种,比较特殊而已。
- 私有化构造函数,且无参数;也就是说,不能显式出构造函数,也不能有次级构造函数;
- 不能
open/abstract
,内部也不能有open
的函数 - 其反编译为
Java
代码,也就是静态饿汉式的单例类写法,线程安全的。
四、枚举类
Kotlin
中的枚举类,类比于Java
的枚举,更为灵活一些
enum class Direction{
NORTH,SOUTH,WEST,EAST//枚举对象用,符号分隔
}
enum class Color(val colorName:String){//这里添加val/var为的是可以称为成员属性参数
YELLOW("#f0f0f0"), GREEN("#00f0f0"), BLUE("#000ff0");
}
类似于
Java
枚举,Kotlin
的枚举可以有参数构造函数-
不同于
Java
枚举,Kotlin
枚举可以有抽象函数,这样每个实例都要override
改函数enum class ColorHHH(val cn: String) { //每个实例,都要override YELLOW("#f0f0f0"){ override fun info() { TODO("Not yet implemented") } }, GREEN("#00f0f0"){ override fun info() { TODO("Not yet implemented") } }, BLUE("#000ff0"){ override fun info() { TODO("Not yet implemented") } }; //定义枚举类的抽象函数,其内部实例,就要override abstract fun info() open fun foo(){} fun boo(){} }
-
EnumClass.valueOf(value:String)
这个函数,入参是String
就是对应枚举实例对象的名字。val co = ColorHHH.valueOf("YELLOW")//这样才能获取到 co.name//就是实例的名字,co.ordinal,就是实例在枚举类中定义的索引编号。
-
枚举类可以实现接口,但是不能继承类。当然也不能
open/abstract
。同2
,如果实现接口,所以实例都要实现,或者枚举类通用实现。interface Color9{ fun fillColor() } interface Shape{ fun defineShape() } //实现上面两个接口 enum class Car:Color9,Shape{ //1、每个实例都实现接口函数的方式 BIZ{ override fun fillColor() { TODO("Not yet implemented") } },BW{ override fun fillColor() { TODO("Not yet implemented") } }; //2,或者就是枚举类自身直接实现接口的函数 override fun defineShape() { } }
五、内部类、嵌套类
-
嵌套类,顾名思义,嵌套在其它类中的定义类。
Kotlin
的特别之处,接口和类可以互相嵌套,也就是类中可定义接口,接口中可定义类。interface OuterInterface { class InnerClass interface InnerInterface } class OuterClass { class InnerClass interface InnerInterface inner class RealInnerClass//这才是对应与Java的内部类,会引用外部类的对象 } //匿名内部类,如果java的new XXX直接用一样 window.addMouseListener(object:MouseAdapter(){ override fun mouseClicked(e: MouseEvent){ //... } override fun mouseEntered(e: MouseEvent){ //... } })
注意,
kotlin
中这么写是嵌套类,在Java
中这么写就是内部类了。 内部类,不同于
Java
中的写法,这里需要一个标记inner
匿名内部类,
Kotlin
中object
的另一作用
附:Kotlin中的类的学用知识点简略如上,更多的学习,知其然知其所以然