Kotlin学习手记——单例、内部类、数据类、枚举类、密封类、内联类

Kotlin单例:

只需要类前面添加object关键字即可,object定义类等价于java的恶汉式的单例模式

object Singleton {    var x: Int = 2     fun y(){ }    init {        //object不能定义构造函数,但可以定义init块    }}

object不能定义构造函数,但可以定义init

使用:

fun main() {    Singleton.x    Singleton.y()}

@JvmStatic@JvmField

object Singleton {    @JvmField var x: Int = 2 //生成java的静态成员 不会生成getter和setter方法    @JvmStatic fun y(){ } //生成java的静态方法}

普通kotlin类(非单例)中使用使用JvmFieldJvmStatic

class Foo {    //普通类中使用JvmField和JvmStatic    companion object {        @JvmField var x: Int = 2        @JvmStatic fun y(){  }    }}

注意,加的注解@JvmField@JvmStatic只针对java平台的可用,其他平台并不可行。

单例的object类仍可以继承类:

object Singleton: Runnable{    override fun run() {    }}

内部类:

在kotlin中类内部的class前面不写修饰符默认就是静态内部类,class前面写 inner修饰符才是java中的普通内部类,与java一样,普通内部类会持有外部类的对象引用。

class Outer {    //普通内部类 与java一样会持有外部类的引用    inner class Inner    //默认静态内部类    class StaticInner}
fun main() {    val inner = Outer().Inner()    val staticInner = Outer.StaticInner()}

object类内部的object类默认是静态的 不存在非静态的情况 不能定义成inner

object OuterObject {    //内部object默认是静态的 不存在非静态的情况 不能定义成inner    object StaticInnerObject}

匿名内部类:

fun main() {    //匿名内部类    object : Cloneable {    }}

其实就是object 省略类名直接实现接口。

匿名内部类 可用继承多个接口,java不行:

fun main() {    // 类型是混合类型:Cloneable & Runnable     val runnableCloneable = object : Cloneable, Runnable {        override fun run() {        }    } }

实现多个接口时,它的类型是多个接口类型的混合类型。

Local class :

fun main() {//本地函数    fun localFunc(){        println("Hello")    }    //对应Java的local class    class LocalClass:  Cloneable, Runnable{        override fun run() {}    }}

可以对比java的local class实现,其实就是在静态函数的内部定义一个类:

public class JavaInnerClasses {    public static void main(String... args) {        class LocalClass implements Cloneable, Runnable {            @Override            public void run() { }        }    }}

说实话,写了这么多年代码,未曾这么干过。。

数据类:

kotlin中提供一个data关键字,data修饰的类就是一个数据类,对标java的bean类:

data class Book(val id: Long, val name: String, val author: Person)data class Person(val id: Long, val name: String, val age: Int)

与java的bean类相比,kotlin的data类不能被继承,并且属性要全部写到构造函数当中,没有无参的构造函数。确实简便了许多!

并且编译器会为data类生成了一些好用的方法:

    val book = Book(0, "Kotlin in Action", Person(1, "Dmitry", 40))    //编译器生成的方法 copy component1    book.copy()    val id = book.component1()    val name = book.component2()    val author = book.component3()

其中copy()component1()等都是编译器自动生成的,component方法的意义是方便解构赋值的使用:

    //解构赋值,对应的字段是通过component方法获取的    val (id, name, author) = book

关于解构:

     //解构    val pair = "Hello" to "World"    val (hello, world) = pair

data不能被继承,那么为啥不能有子类呢?

可以先看一下kotlin为data类生成的对应的java类是什么样的,查看方式,doule shift键,然后Actions中输入kotlin Bytecode显示:

点击Decompile即可查看对应生成的java代码

以下是Book的数据类对应生成的java代码

public final class Book {   private final long id;   @NotNull   private final String name;   @NotNull   private final Person author;   public final long getId() {      return this.id;   }   @NotNull   public final String getName() {      return this.name;   }   @NotNull   public final Person getAuthor() {      return this.author;   }   public Book(long id, @NotNull String name, @NotNull Person author) {      Intrinsics.checkNotNullParameter(name, "name");      Intrinsics.checkNotNullParameter(author, "author");      super();      this.id = id;      this.name = name;      this.author = author;   }   public final long component1() {      return this.id;   }   @NotNull   public final String component2() {      return this.name;   }   @NotNull   public final Person component3() {      return this.author;   }   @NotNull   public final Book copy(long id, @NotNull String name, @NotNull Person author) {      Intrinsics.checkNotNullParameter(name, "name");      Intrinsics.checkNotNullParameter(author, "author");      return new Book(id, name, author);   }   // $FF: synthetic method   public static Book copy$default(Book var0, long var1, String var3, Person var4, int var5, Object var6) {      if ((var5 & 1) != 0) {         var1 = var0.id;      }      if ((var5 & 2) != 0) {         var3 = var0.name;      }      if ((var5 & 4) != 0) {         var4 = var0.author;      }      return var0.copy(var1, var3, var4);   }   @NotNull   public String toString() {      return "Book(id="   this.id   ", name="   this.name   ", author="   this.author   ")";   }   public int hashCode() {      long var10000 = this.id;      int var1 = (int)(var10000 ^ var10000 >>> 32) * 31;      String var10001 = this.name;      var1 = (var1   (var10001 != null ? var10001.hashCode() : 0)) * 31;      Person var2 = this.author;      return var1   (var2 != null ? var2.hashCode() : 0);   }   public boolean equals(@Nullable Object var1) {      if (this != var1) {         if (var1 instanceof Book) {            Book var2 = (Book)var1;            if (this.id == var2.id && Intrinsics.areEqual(this.name, var2.name) && Intrinsics.areEqual(this.author, var2.author)) {               return true;            }         }         return false;      } else {         return true;      }   }}

除了类名方法名前面添加了final关键字以外,生成了许多方法,其中有重写hashCode()equals()方法,所以如果有一个类继承了data类,可能导致属性变化,从而导致hashCode()equals()方法的结果不一致。

data类的属性最好全部为基本类型或者其他data类型,保持它的纯净性。

另外,有方法可以破除data类的不可继承性,也有网友在吐槽kotlin的这个设计,觉得它不好,其实如果你想用一个可以继承到类,只需要把data关键字去掉,建一个普通类就好了。kotlin这样设计肯定是想保持它的纯洁性,如果可继承,只会变的更复杂。

破除data类的不可继承性需要额外添加两个插件:

plugins {...    id 'org.jetbrains.kotlin.plugin.noarg' version '1.4.20'    id 'org.jetbrains.kotlin.plugin.allopen' version '1.4.20'}noArg {    invokeInitializers = true    annotations "com.bennyhuo.kotlin.advancedtypes.dataclasses.PoKo"}allOpen {    annotations "com.bennyhuo.kotlin.advancedtypes.dataclasses.PoKo"}

上面是参考学习资料当中的代码工程中的配置,然后在data类上加注解即可

@PoKodata class Book(val id: Long, val name: String, val author: Person)

这时同样去查看Book的数据类对应生成的java代码会发现,之前的类名和get方法名前面的final关键字被移除了,也就是可以被继承使用了,同时会生成一个无参的构造函数。可见比较麻烦,非有必要,还是不要这么干。。

枚举类:

kotlin里面的枚举类跟java差不多

enum class State {    Idle, Busy}//枚举定义构造函数 同javaenum class State1(val id: Int) {    Idle(0), Busy(1)}enum class Color {    White, Red, Green, Blue, Yellow, Black}
fun main() {    State.Idle.name // Idle    State.Idle.ordinal // 0    val state = State.Idle    //枚举全部值    val value = when (state) {        State.Idle -> { 0 }        State.Busy -> { 1 }    }    //枚举创建区间    val colorRange = Color.White .. Color.Green    val color = Color.Blue //Blue不在区间内    println(color in colorRange)}

枚举类不可继承其他类,因为枚举类有父类是enum,但是可以实现接口:

enum class State: Runnable{    Idle, Busy{        override fun run() {            println("For Busy State.")        }    };    override fun run() {        println("For  Every State.")    }}
fun main() {    State.Idle.run()    State.Busy.run()}

枚举类可以定义扩展函数:

fun State.successor(): State? {    return State.values().let {        if (ordinal   1 >= it.size) null        else it[ordinal   1]    }}fun State.predecessor(): State? {    return State.values().let {        if (ordinal - 1 < 0) null        else it[ordinal - 1]    }}
fun main() {    println(State.Idle.successor())    println(State.Busy.successor())}

密封类:

其实就是一个只能在同一个文件中定义子类的抽象类。不得不说kotlin玩的花样很多,会玩。。。

定义方式是在类的前面加sealed关键字

sealed class PlayerStateobject Idle : PlayerState()class Playing(val song: Song) : PlayerState() {    fun start() {}    fun stop() {}}class Error(val errorInfo: ErrorInfo) : PlayerState() {    fun recover() {}}

完整的示例:控制播放器播放状态的例子

data class Song(val name: String, val url: String, var position: Int)data class ErrorInfo(val code: Int, val message: String)object Songs {    val StarSky = Song("Star Sky", "https://fakeurl.com/321144.mp3", 0)}sealed class PlayerStateobject Idle : PlayerState()class Playing(val song: Song) : PlayerState() {    fun start() {}    fun stop() {}}class Error(val errorInfo: ErrorInfo) : PlayerState() {    fun recover() {}}class Player {    var state: PlayerState = Idle    fun play(song: Song) {        this.state = when (val state = this.state) {            Idle -> {                Playing(song).also(Playing::start)            }            is Playing -> {                state.stop()                Playing(song).also(Playing::start)            }            is Error -> {                state.recover()                Playing(song).also(Playing::start)            }        }    }}fun main() {    val player = Player()    player.play(Songs.StarSky)}

注意其中的when表达式的使用,可见它可以用来替代枚举类做类似的功能,子类的个数也是全部可枚举的。跟枚举类有相似之处。

内联类:

简而言之,内联类是一种类型的包装类,类前面加inline关键字,构造器只能有一个参数,不能继承类,不能被继承,不能作为内部类。类似于java的Integer、Double这种吧,但有不同。

内联类虽然不能继承类或被继承,但是可以实现接口。

////只能有方法不能有属性inline class BoxInt(val value: Int): Comparable<Int> {    override fun compareTo(other: Int)            = value.compareTo(other)    operator fun inc(): BoxInt {        return BoxInt(value   1)    }}

BoxInt会做编译优化,只有在需要的时候才使用包装类 多数情况下直接使用Int,可以反编译字节码生成的java代码查看。另外,还有个特点就是它只能有方法不能有属性。

内联类也可以用来模拟枚举类,与枚举相比内存占用小,但使用方式类似

inline class State(val ordinal: Int) {    companion object {        val Idle = State(0)        val Busy = State(1)    }    fun values() = arrayOf(Idle, Busy)    val name: String        get() = when (this) {            State.Idle -> "Idle"            State.Busy -> "Busy"            else -> throw  IllegalArgumentException()        }}inline class Color(val value: UInt) {    companion object {        val Red = Color(0xFFFF0000u)        val Green = Color(0xFF00FF00u)        val Blue = Color(0xFF0000FFu)    }    fun values() = arrayOf(Red, Green, Blue)    val name: String        get() = when (this) {            Red -> "Red"            Green -> "Green"            Blue -> "Blue"            else -> throw  IllegalArgumentException()        }}
fun main() {    State.Busy    Color.Blue    var boxInt = BoxInt(5)    if(boxInt < 10){        println("value is less than 10")    }    val newValue = boxInt.value * 200    println(newValue)    boxInt      println(boxInt)}

密封类的实例:递归整型列表的简单实现

sealed class IntList {    object Nil: IntList() {        override fun toString(): String {            return "Nil"        }    }    data class Cons(val head: Int, val tail: IntList): IntList(){        override fun toString(): String {            return "$head, $tail"        }    }    fun joinToString(sep: Char = ','): String {        return when(this){            Nil -> "Nil"            is Cons -> {                "${head}$sep${tail.joinToString(sep)}"            }        }    }}fun IntList.sum(): Int {    return when(this){        IntList.Nil -> 0        is IntList.Cons -> head   tail.sum()    }}operator fun IntList.component1(): Int? {    return when(this){        IntList.Nil -> null        is IntList.Cons -> head    }}operator fun IntList.component2(): Int? {    return when(this){        IntList.Nil -> null        is IntList.Cons -> tail.component1()    }}operator fun IntList.component3(): Int? {    return when(this){        IntList.Nil -> null        is IntList.Cons -> tail.component2()    }}fun intListOf(vararg ints: Int): IntList {    return when(ints.size){        0 -> IntList.Nil        else -> {            IntList.Cons(                ints[0],                //array前面加* 展开数组                intListOf(*(ints.slice(1 until ints.size).toIntArray()))            )        }    }}// [0, 1, 2, 3]fun main() {    //val list = IntList.Cons(0, IntList.Cons(1,  IntList.Cons(2,  IntList.Cons(3, IntList.Nil))))    val list = intListOf(0, 1, 2, 3)    println(list)    println(list.joinToString('-'))    println(list.sum())    val (first, second, third) = list    println(first)    println(second)    println(third)    //val (a, b, c, d, e) = listOf<Int>()}

来源:https://www.icode9.com/content-4-797401.html

(0)

相关推荐