Kotlin学习(五):kotlin类详解进阶

系列文章路引 https://blog.csdn.net/weixin_44235109/article/details/119680974

文章目录

    • 系列文章路引 https://blog.csdn.net/weixin_44235109/article/details/119680974
  • 一、kotlin类的构造器?
    • 1.构造器的基本写法与java对比?
    • 2.init代码块
    • 3.kotlin副构造器
    • 4.kotlin构造器的推荐使用方式
  • 二、Kotlin与Java类与成员的可见性对比?
    • 1.Kotlin与Java可见性修饰符对比?
    • 2.Kotlin构造器以及属性的可见性?
  • 三、kotlin类属性的延迟初始化?
    • 1.为什么要延迟初始化?
    • 2.定义可null类型延迟初始化
    • 3.使用lateinit初始化
    • 4.使用lazy延迟初始化(推荐)
    • 5.延迟初始化方案对比
  • 四、Kotlin代理Delegate
    • 1.代理是什么?
    • 2.接口代理
    • 2.属性代理
      • 属性代理基本理解
      • 常见属性代理基本使用
  • 五、Kotlin单例object
    • 0.object、companion object以及val和const val的区别
    • 1.单例的多种实现方式
    • 2.kotlin的单例模式object
    • 3.object单例模式的简单使用
    • 4.java访问object单例
    • 4.kotlin中对应java的静态成员
  • 五、Kotlin内部类
    • 1.Java与Kotlin实现内部类方式比较
    • 2.Kotlin内名内部类
  • 六、Kotlin数据类data
    • 1.Java与Kotlin实现内部类方式比较
    • 2.data class 注意点
  • 七、枚举类EnumClass
    • 1.Java与Kotlin实现枚举类比较
    • 2.Kotlin枚举的简单使用
  • 八、密封类sealed class
    • 1.密封类概念
    • 2.密封类简单使用
  • 九、内联类inline
    • 1.内联类概念
    • 2.内联类定义
    • 3.内联类的限制
    • 4.内联类使用例子
  • 十、尝试使用component实现解构
  • 总结

一、kotlin类的构造器?

1.构造器的基本写法与java对比?

示例:kotlin类构造器的常规写法

//大括号包含的即是kotlin的构造器了,声明了两个属性
//1.age 类内全局可见  2.构造器内(init块,属性初始化)
//    类的构造器  构造器参数 同时也定义了属性 
class Person constructor(var age: Int, var str: String)

//    省略写法
class Person(var age: Int, name: String)

这么看下去有点懵?我们翻译成java代码看一下
示例:翻译为java代码如下

public final class Person {
   private int age;

   public final int getAge() {
      return this.age;
   }

   public final void setAge(int var1) {
      this.age = var1;
   }

   public Person(int age, @NotNull String name) {
      Intrinsics.checkNotNullParameter(name, "name");
      super();
      this.age = age;
   }
}

这就看得懂了吧,java需要这么多行的代码,kotlin一行就搞定了。是不是很方便。
那有些小伙伴就问了,那如果我想在java的构造函数中处理一些逻辑,kotlin怎么做到的呢?
那这个时候init代码块就出现了,具体是什么,我们一块来看看吧~

2.init代码块

示例:init代码块

class Person(var age: Int, name: String) {
    var name: String? = null

    init {
        println(name)
    }

    init {
        val nameStr = name
        this.name = nameStr
    }
}

我们可以看到init代码块可以访问到构造器传入的参数,那么他是怎么样被调用的呢?对应的java代码是什么呢?我们反编译成java代码看一下

public final class Person {
   @Nullable
   private String name;
   private int age;

   @Nullable
   public final String getName() {
      return this.name;
   }

   public final void setName(@Nullable String var1) {
      this.name = var1;
   }

   public final int getAge() {
      return this.age;
   }

   public final void setAge(int var1) {
      this.age = var1;
   }

   public Person(int age, @NotNull String name) {
      Intrinsics.checkNotNullParameter(name, "name");
      super();
      this.age = age;
      boolean var3 = false;
      System.out.println(name);
      this.name = name;
   }
}

可以很明显的看出,init代码块在Java平台其实就是相当于构造函数的执行体,且如果有多个init块,是按照顺序在构造函数中执行的。

3.kotlin副构造器

定义:定义了主构造器后在类内部定义的构造器统称为副构造器(定义的副构造器需要调用自身(最终调用到主构造器)或者父类的构造器)
示例:副构造器的简单示例

class Person(var age: Int, name: String) {
    constructor(age: Int) : this(age, "zxf"){
        println("i am sub-constructor")
    }

    init {
        println(name)
    }
}

我们看一下对应的java代码如何

public final class Person {
   private int age;

   public final int getAge() {
      return this.age;
   }

   public final void setAge(int var1) {
      this.age = var1;
   }

   public Person(int age, @NotNull String name) {
      Intrinsics.checkNotNullParameter(name, "name");
      super();
      this.age = age;
      boolean var3 = false;
      System.out.println(name);
   }

   public Person(int age) {
      this(age, "zxf");
      String var2 = "i am sub-constructor";
      boolean var3 = false;
      System.out.println(var2);
   }
}

对应的java代码其实就是声明了一个对应的构造器,且调用了“主”构造器。
另外看得出,init代码块只在主构造器中被调用,且因为副构造器代码块中的第一行代码。首先调用了“主”构造器。
所以代码执行顺序:init代码块要先与副构造器执行

4.kotlin构造器的推荐使用方式

  • 不推荐不定义主构造器只定义副构造器
  • 推荐主构造器填上默认参数

推荐示例

class Person(var age: Int, name: String = "zxf")

//主构造器默认参数在java代码中可以以重载的形式调用
class Person1 @JvmOverloads constructor(var age: Int, name: String = "zxf", height: Int = 185)

二、Kotlin与Java类与成员的可见性对比?

1.Kotlin与Java可见性修饰符对比?

可见性表格对比:

可见性对比 Java Kotlin
public 公开 与java相同,默认即为public
internal × 模块内可见
default 包内可见,默认 ×
protected 包内以及子类可见 类内以及子类可见(不可修饰顶级类)
private 类内可见 类内或者文件内可见

模块内通常来说是一个jar包或者aar,比如:

  • Intellij IDEA 模块
  • Maven 工程
  • Gradle SourceSet
  • Ant 任务中一次调用 文件

下面针对kotlin的特有的修饰符,看一下可修饰的对象

可见性类型 顶级声明 成员
public
internal √(模块内可见) √(模块内可见) √(模块内可见)
protected × √(可修饰内部类,类、以及子类可见) √(类、以及子类可见)
private √(文件) √(文件) √(类)

顶级声明:指文件内直接定义的属性、函数、类、等

internal VS default

  • 一般由SDK或者公共组件开发者用于隐藏模块内部细节实现
  • default 可通过外部创建相同包名来进行访问,访问控制非常弱
  • default 会导致不同抽象汇聚到相同的包下面
  • internal 可方便处理内外隔离,提升模块代码内聚减少接口暴露
  • internal 修饰的kotlin类或者成员在java当中可直接访问

2.Kotlin构造器以及属性的可见性?

正常的构造器默认都是public,那如何控制构造器的访问可见性呢?如下所示:

//默认为public修饰
class Person(val name: String, val age: Int)
//构造器私有化
class Person private constructor(val name: String, val age: Int)

属性可见性

//私有化属性,外部不可以进行访问
class Peron(private var age:Int,var name:String)
  • getter的可见性必须与属性保持一致
  • setter的可见性不得大于属性的可见性
    如下所示:
    Kotlin学习(五):kotlin类详解进阶_第1张图片
    Kotlin学习(五):kotlin类详解进阶_第2张图片

三、kotlin类属性的延迟初始化?

1.为什么要延迟初始化?

类属性必须在构造时进行初始化,但是某些成员只有在类构造之后才会有值,所以需要延迟初始化

2.定义可null类型延迟初始化

如下所示:

    private var name: String? = null
    fun onCreate() {
        //赋值
        name = "zxf"
        //使用
        useName(name!!)
        useName(name ?: "")
    }
    fun useName(name: String) {
        // TODO: 2021/7/5 do
    }

3.使用lateinit初始化

如下所示:使用处不再需要空指针判断

    private lateinit var name: String

    fun onCreate() {
        //赋值
        name = "zxf"
        //使用
        useName(name)
        useName(name)
    }

    fun useName(name: String) {
        // TODO: 2021/7/5 tdo 
    }

注意:使用lateinit必须知道属性初始化以及使用的生命周期,如果在属性还没有初始化的时候进行使用,则会出现如下的异常:lateinit property name has not been initialized

Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property name has not been initialized
	at org.example.zxf.kotlin5.DelayInitTest.onCreate(DelayInit.kt:22)
	at org.example.zxf.kotlin5.DelayInitKt.main(DelayInit.kt:13)
	at org.example.zxf.kotlin5.DelayInitKt.main(DelayInit.kt)

另外,可以使用isInitialized判断属性是否初始化
如下所示

fun onCreate() {
    //判断是否初始化
    if (::name.isInitialized){
        name = "zxf"
    }        
    //使用
    useName(name)
}

4.使用lazy延迟初始化(推荐)

如下所示:

class Personal4(var age: Int) {
    //  延迟初始化   只有在该属性第一次被访问到时,进行初始化
    val name: String by lazy {
        "zxf"
    }
}

fun main() {
//    使用该属性
    println(Personal4(2).name)
}

注意:如果确保被访问的属性不存在线程安全的问题,可传入LazyThreadSafetyMode.NONE参数,因为默认lazy使用synchronized做同步,若不存在线程安全的问题,传入LazyThreadSafetyMode.NONE可不做同步。
如下所示:

class Personal4(var age: Int) {
    //  延迟初始化   只有在该属性第一次被访问到时,进行初始化
    val name: String by lazy(LazyThreadSafetyMode.NONE) {
        "zxf"
    }
}

fun main() {
//    使用该属性
//    println(Personal4(2).name)
    DelayInitTest().onCreate()
}

5.延迟初始化方案对比

方案名称 推荐指数 理由
可空类型 ⭐⭐ 增加代码复杂度;初始化与声明分离;调用处需要做判空处理
lateinit ⭐⭐⭐ 初始化与声明分离;调用处无需判空处理,但潜在的初始化问题可能被掩盖
lazy ⭐⭐⭐⭐⭐ 初始化与声明内聚;无需声明可空类型

四、Kotlin代理Delegate

1.代理是什么?

接口代理:对象X代替当前类A实现接口B的方法
属性代理:对象X代替属性a实现getter/setter方法
关键字by:by关键字实际上就是一个代理运算符重载的符号,任何一个具备代理规则的类,都可以使用by关键字进行代理。

2.接口代理

使用传入的对象去代理接口。
Kotlin学习(五):kotlin类详解进阶_第3张图片
如下所示:

//定义接口
interface Api {
    fun a()
    fun b()
    fun c()
}
//定义实现类1
class ApiImpl : Api {
    override fun a() {}
    override fun b() {}
    override fun c() {
        println("c is ApiImpl.")
    }
}
//定义实现类2
class ApiImpl1 : Api {
    override fun a() {}
    override fun b() {}
    override fun c() {
        println("c is ApiImpl1.")
    }
}
//使用接口代理实现类包装
class ApiWrapper(private val api: Api) : Api by api {
    override fun c() {
        println("c is ApiWrapper.")
        api.c()
    }
}
//使用以及结果
fun main(){
    ApiWrapper(ApiImpl()).c()
    ApiWrapper(ApiImpl1()).c()
}
c is ApiWrapper.
c is ApiImpl.
c is ApiWrapper.
c is ApiImpl1.

2.属性代理

属性代理基本理解

属性代理是借助于代理设计模式,把这个模式应用于一个属性时,它可以将访问器的逻辑代理给一个辅助对象。
可以简单理解为属性的setter、getter访问器内部实现是交给一个代理对象来实现,相当于使用一个代理对象来替换了原来简单属性字段读写过程,而暴露外部属性操作还是不变的,照样是属性赋值和读取,只是setter、getter内部具体实现变了
属性代理的基本格式:

class Student{
    var name: String by Delegate()
}

class Delegate{
    operator fun <T> getValue(thisRef: Any?, property: KProperty<*>): T{
        ...
    }
    operator fun <T> setValue(thisRef: Any?, property: KProperty<*>, value: T){
        ...
    }
}
属性name将它访问器的逻辑委托给了Delegate对象,通过by关键字对表达式Delegate()求值获取这个对象。任何符合属性代理规则都可以使用by关键字。属性代理类必须要遵循getValue(),setValue()方法约定,getValue、setValue方法可以是普通方法也可以是扩展方法,并且是方法是支持运算符重载。如果是val修饰的属性只需要具备getValue()方法即可。

属性代理基本流程就是代理类中的getValue()方法包含属性getter访问器的逻辑实现,setValue()方法包含了属性setter访问器的逻辑实现。当属性name执行赋值操作时,会触发属性setter访问器,然后在setter访问器内部调用delegate对象的setValue()方法;执行读取属性name操作时,会在getter访问器中调用delegate对象的getValue方法.

常见属性代理基本使用

属性代理是Kotlin独有的特性,我们自己去自定义属性代理,当然Kotlin还提供了几种常见的属性代理实现。例如:lazy,Delegates.observable(), Delegates.vetoable() **属性代理-lazy**:代理主要用于可以初始化时候初始化而是可以延迟到之后再初始,这个在上面将延迟初始化的时候已经提到过了,这里就不多做介绍了。 **属性代理-observable**:Delegates.observable()主要用于监控属性值发生变更,类似于一个观察者。当属性值被修改后会往外部抛出一个变更的回调。它需要传入两个参数,一个是initValue初始化的值,另一个就是回调lamba, 回调出property, oldValue, newValue三个参数。

示例如下:

//    属性代理 observable
class StateManager {
    //    initialValue  初始值
    var state: Int by Delegates.observable(0) {
//        set  的时候执行当前的表达式
            property, oldValue, newValue ->
        println(
            """property:$property
                   oldValue:$oldValue
                   newValue:$newValue
        """.trimMargin()
        )
    }
}

//使用
fun main() {
    val stateManager = StateManager()
    stateManager.state = 10
    stateManager.state = 11
}

//输出
property:var org.example.zxf.kotlin5.StateManager.state: kotlin.Int
                   oldValue:0
                   newValue:10
property:var org.example.zxf.kotlin5.StateManager.state: kotlin.Int
                   oldValue:10
                   newValue:11

属性代理-vetoable:Delegates.vetoable()代理主要用于监控属性值发生变更,类似于一个观察者,当属性值被修改后会往外部抛出一个变更的回调。它需要传入两个参数,一个是initValue初始化的值,另一个就是回调lamba, 回调出property, oldValue, newValue三个参数。与observable不同的是这个回调会返回一个Boolean值,来决定此次属性值是否执行修改。

示例如下:

class StateManager {

    var state2: Int by Delegates.vetoable(0) { 
            property, oldValue, newValue ->
        return@vetoable newValue == 1
    }
}

//使用
fun main(){
	val stateManager2 = StateManager()
    stateManager2.state2 = 1
    stateManager2.state2 = 2
    println("stateManager2 state2 is :${stateManager2.state2}")
}

//输出
stateManager2 state2 is :1

五、Kotlin单例object

0.object、companion object以及val和const val的区别

object单例

默认有一个INSTATIC的静态成员常量(static final),在静态代码块中,分配自己(new 自己)

成员变量 val 相当于 java类里面的 声明了静态常量(static final),在静态代码块中初始化值

成员变量var相当于java类里面的 声明了静态变量(static),在静态代码块中初始化值。

方法都是对象方法。

companion object伴生对象理论上来说和object一样

在当前类里面创建了一个Companion 的类,在当前类中,持有一个静态常量(static final),静态字段初始化的时候new Companion 对象

里面的成员变量val 和 var 和上面的object相似,不过是在当前类中声明的,在当前类的静态代码块中初始化。

方法在用final修饰的,在Companion 对象内部声明。

这里面的 const val 和 val的区别

const val 是以静态字段的形式初始化(编译期常量)

val 在静态代码块中初始化(运行时常量)

1.单例的多种实现方式

饿汉式:线程安全,可用。但类一加载的时候,就实例化,提前占用了系统资源
示例如下(Java):

public class Singleton {
    private Singleton() {
    }

    private static Singleton sSingleton = new Singleton();

    public static Singleton getInstance() {
        return sSingleton;
    }
}

懒汉式:一般配合双重检查锁 DCL使用,保证线程安全/font>
示例如下(Java):

public class Singleton {
    private Singleton() {
    }
    
    private static volatile Singleton sSingleton;

    public static Singleton getInstance() {
        if (sSingleton == null) {
            synchronized (Singleton.class) {
                // 未初始化,则初始instance变量
                if (sSingleton == null) {
                    sSingleton = new Singleton();
                }
            }
        }
        return sSingleton;
    }
}

静态内部类:使用时才会加载,且加载类时初始化,线程安全
示例如下(Java):

public class Singleton {

    private Singleton () {
    }

    private static class InnerClassSingleton {
     private final static Singleton sSingleton = new Singleton();
    }

    public static Singleton getInstance() {
        return InnerClassSingleton.sSingleton;
    }
}

2.kotlin的单例模式object

单例模式:kotlin使用object修饰的单例模式采用的是饿汉式
示例如下

object Singleton {
}

反编译为java代码如下所示

public final class Singleton {
   public static final Singleton INSTANCE;

   private Singleton() {
   }

   static {
      Singleton var0 = new Singleton();
      INSTANCE = var0;
   }
}

可以看的出,反编译之后为饿汉式的方式实现单例的。

3.object单例模式的简单使用

简单使用:一般都用做工具类使用(object不能定义构造器)
示例如下

//声明单例类
object Singleton {

    var age: Int = 0

    var name: String = ""

    fun test() {
		println("test")
    }
}
//使用单例类
fun main() {
    Singleton.age = 10
    Singleton.name = "zxf"
    println(Singleton.name)
    Singleton.test()
}
//输出
zxf
test
age:1

4.java访问object单例

那么java如何访问上述的单例类呢?
示例如下

 Singleton.INSTANCE.getAge();
 Singleton.INSTANCE.getName();
 Singleton.INSTANCE.setName("zxf");
 Singleton.INSTANCE.test();

上述是不是太麻烦了,有没有简单的方式呢?当然有,使用JvmField或者JvmStatic注解即可
示例如下

object Singleton {

    //JvmField 对于java来说不生成getter/setter方法  访问等同于java Field
    @JvmField
    var age: Int = 0

    var name: String = ""

    //JvmField object的成员直接按照java静态成员生成字节码,对kotlin内部毫无影响,对于java可直接视为调用静态成员一般
    @JvmStatic
    fun test() {
        println("test")
    }
}
//java调用
Singleton.age = 10;
Singleton.test();
Singleton.INSTANCE.getName();

4.kotlin中对应java的静态成员

companion伴生对象,一般是 companion object 类似于 java静态变量方法的使用
示例如下

//java
public class Foo{
	public static int age;
	public static void y(){}
}

//kotlin 对应写法
class Foo{
	companion object {
		@JvmField
        var age: Int = 0
        
		@JvmStatic 
		fun y(){}
	}
}

五、Kotlin内部类

1.Java与Kotlin实现内部类方式比较

模式:默认是静态内部类,加上inner 是非静态内部类
示例如下:

//java
public class Outer{
	//非静态内部类,实例持有外部类实例引用
	class Inner{}
	//静态内部类
	static class StaticInner{}
}
//kotlin
class Outer{
	//非静态内部类,实例持有外部类实例引用
	inner class Inner
	//静态内部类
	class StaticInner
}

2.Kotlin内名内部类

示例如下:

val runnable = object : Runnable {
    override fun run() {
        TODO("Not yet implemented")
    }
}

kotlin匿名内部类:可以继承类或者实现多个接口

 val value = object : Runnable, RemoteJVMLauncher.CallBack {
     override fun run() {
         TODO("Not yet implemented")
     }

     override fun jvmStarted() {
         TODO("Not yet implemented")
     }
 }

六、Kotlin数据类data

1.Java与Kotlin实现内部类方式比较

数据类,不能够被继承,没有无参构造方法。可以进行解构操作、重新了equals、hashcode、toString、提供getter、setter、copy等函数
简单示例如下:

//java
public class Book{
	private long id;
	private String name;
	private String person;
	//省略getter/setter方法
}
//kotlin
data class Book(
	var id:Long,
	var name:String,
	var person:String
)
//kotlin 解构的简单使用
 val (id, name, person) = Book(1L, "zxf", "person")
 println("id:$id,name:$name,person:$person")
 //输出
 id:1,name:zxf,person:person

2.data class 注意点

我们可以查看data class反编译之后的代码

public final class Book {
   private final long id;
   @NotNull
   private final String name;
   @NotNull
   private final String person;

   public final long getId() {
      return this.id;
   }

   public final void setId(long var1) {
      this.id = var1;
   }
   //其他getset省略

   public Book(long id, @NotNull String name, @NotNull String person) {
      Intrinsics.checkNotNullParameter(name, "name");
      Intrinsics.checkNotNullParameter(person, "person");
      super();
      this.id = id;
      this.name = name;
      this.person = person;
   }

   public final long component1() {
      return this.id;
   }

   @NotNull
   public final String component2() {
      return this.name;
   }

   @NotNull
   public final String component3() {
      return this.person;
   }

   @NotNull
   public final Book copy(long id, @NotNull String name, @NotNull String person) {
      Intrinsics.checkNotNullParameter(name, "name");
      Intrinsics.checkNotNullParameter(person, "person");
      return new Book(id, name, person);
   }

   // $FF: synthetic method
   public static Book copy$default(Book var0, long var1, String var3, String 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.person;
      }

      return var0.copy(var1, var3, var4);
   }

   @NotNull
   public String toString() {
      return "Book(id=" + this.id + ", name=" + this.name + ", person=" + this.person + ")";
   }

   public int hashCode() {
     //todo 省略
   }

   public boolean equals(@Nullable Object var1) {
   	//todo 省略
   }
}

除了常规的方法外,还提供了几个component,上面我们使用的解构的功能,就是通过component实现的。
Kotlin学习(五):kotlin类详解进阶_第4张图片

  • 基于component提供相等性,解构等功能
  • 数据类属性类型最好为基本类型、string、或者其他dataclass
  • Component不可以自定义getter、setter
  • 定义成员最好定义为val,防止修改后equals和hashcode的相等性判断错误

七、枚举类EnumClass

1.Java与Kotlin实现枚举类比较

java与kotlin实现枚举对比
简单示例如下:

//java
enum State{
	InComplete,Complete
}
//使用
State.InComplete.name()//InComplete
State.InComplete.ordinal()//0

//kotlin
enum State{
	InComplete,Complete
}
//使用
State.InComplete.name//InComplete
State.InComplete.ordinal//0

定义构造器示例如下:

//java
enum State{
	InComplete(0),Complete(1);
	int id;
	State(int id){
	this.id = id;
	}
}
//kotlin
enum class State(val id: Int){
	InComplete(0),Complete(1)
}

2.Kotlin枚举的简单使用

//    定义枚举类
enum class Color {
    WHITE, BLACK, RED;
}

//    为枚举定义扩展方法
fun Color.nextColor(): Color {
//    values()以集合的形式返回出所有的枚举
    return Color.values().let {
//        ordinal 当前的序号
        val nextOrdinal = (this.ordinal + 1) % it.size
        it[nextOrdinal]
    }
}

fun main() {

//    获取枚举的位置和名字
    val ordinal = Color.BLACK.ordinal
    val name = Color.BLACK.name
    println("enum:name:$name,index:$ordinal")

//    枚举比较大小  按照序号比较
    println(Color.BLACK > Color.RED)
//    使用扩展方法获取下一个
    println("whitenextcolor:${Color.WHITE.nextColor().name}")
//    枚举的区间
    val closedRange = Color.WHITE..Color.RED

}

八、密封类sealed class

1.密封类概念

  • 密封类是一种特殊的抽象类
  • 密封类的子类定义在与自身相同的文件中
  • 密封类的子类个数是有限的

密封类构造器是私有的,子类只能写在同一个文件夹里面
密封类构VS枚举类

密封类 枚举类
状态实现 子类继承 类实例化
状态可数 子类可数 实例可数
状态差异 类型差异 值差异

2.密封类简单使用

简单示例如下:状态机流转

//    密封类
sealed class PlayerState

//    定义子类
object Idle : PlayerState()
class Playing(val song: String) : PlayerState() {
    fun stop() {}
    fun start() {}
}

class Error(val song: String) : PlayerState() {
    fun recover() {}
}

class Player(var state: PlayerState = Idle) {
    fun play(song:String){
        this.state = when(state){
            Idle -> Playing(song).apply { this.start() }
            is Playing -> {
                (state as Playing).stop()
                Playing(song).also { it.start() }
            }
            is Error ->{
                (state as Error).recover()
                Playing(song).apply { this.start() }
            }

        }
    }
}

九、内联类inline

1.内联类概念

  • 内联类是对某一类型的包装
  • 内联类是类似java装箱类型的一种类型
  • 编译器会尽可能使用被包装的类型进行优化
  • 内联类在1.3中处于公测阶段,谨慎使用(内联类在1.5正式发布)

2.内联类定义

示例如下

//包装Int类型        必须是val    
inline class BoxInt(val value:Int)

内联类可以实现接口,但不能继承父类也不能被继承
继承简单示例如下

inline class BoxInt(val value:Int):Comparable<Int>{
	override fun compareTo(other:Int){
		//todo
	}
}

3.内联类的限制

  • 主构造器必须有且仅有一个只读属性
  • 不能定义有backing-field的其他属性
  • 被包装类型必须不能是泛型类型
  • 不能继承父类也不能被继承
  • 内联类不能定义为其他类的内部类

typealias VS inline class

typealias inline class
类型 没有新类型 有包装类型产生
实例 与原类型一致 必要时使用包装类型
场景 类型更直观 优化包装类型性能

4.内联类使用例子

package org.example.zxf.kotlin5

//    内联类
inline class BoxInt(val value: Int) : Comparable<Int> {

    override fun compareTo(other: Int): Int {
        return value.compareTo(other)
    }

    operator fun inc(): BoxInt {
        return BoxInt(value + 1)
    }

}

//    使用内联类模拟枚举 减少开销
inline class EgEnum(val value: Int){
    
}

class Do {
    companion object {
        val ONE: EgEnum = EgEnum(0)
        val TWO: EgEnum = EgEnum(1)
        val THREE: EgEnum = EgEnum(2)
    }

    fun useEgEnum(other: EgEnum) {
        println(other)
    }
}

fun main() {
    val boxInt = BoxInt(5)
    if (boxInt < 10) {
        println("value is less than 10")
    }

    val value = Do()
    value.useEgEnum(Do.ONE)
}

十、尝试使用component实现解构

上面在将data class的时候,我们只有有一个解构的功能,和component有关,通过上面的源码,我们知道data class 就是通过component实现的解耦,那么我们能不能自己实现解耦呢?
当然是可以的,首先component是什么?
其实component也是运算符,我们可以看一下list扩展函数的component的实现。

val (x,y) = ArrayList<String>()

// 对应kotlin的源码
/**
 * Returns 1st *element* from the list.
 * 
 * Throws an [IndexOutOfBoundsException] if the size of this list is less than 1.
 */
@kotlin.internal.InlineOnly
public inline operator fun <T> List<T>.component1(): T {
    return get(0)
}

/**
 * Returns 2nd *element* from the list.
 * 
 * Throws an [IndexOutOfBoundsException] if the size of this list is less than 2.
 */
@kotlin.internal.InlineOnly
public inline operator fun <T> List<T>.component2(): T {
    return get(1)
}

可以看出其实就是将component作为运算符重载了,我们如果需要实现,只需要照葫芦画瓢就可以了。简单吧。

总结

以上便是Kotlin类型相关的东西了。下面可以协议demo。做一个简单的递归列表吧,可以指运行噢。

package org.example.zxf.kotlin5

//    密封类 是特殊的抽象类 可以被继承  只能在本文件内部使用
sealed class IntList {
    /**
     * 接口里面 fun test() 的方法默认为 public abstract 修饰的
     * 抽象类里面 如果不屑abstract或者open 则默认不可以被重写
     *
     * 默认内部类为静态 用inner修饰的为非静态
     * object 修饰饿汉式单例
     */
    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:$head,tail:$tail)"""
        }
    }

    fun intJoinToString(sep: Char): String {
        return when (this) {
            Nil -> "NIL"
            is Cons -> {
//                this  可以访问 因为编译器自动做了类型转换 转换为了Cons
                "${this.head}${sep}${this.tail.intJoinToString(sep)}"
            }
        }
    }

}

//    求和
fun IntList.sum(): Int {
    return when (this) {
        IntList.Nil -> 0
        is IntList.Cons -> {
            this.head + this.tail.sum()
        }
    }
}
//    定义解构

operator fun IntList.component1(): Int {
    return when (this) {
        IntList.Nil -> 0
        is IntList.Cons -> this.head
    }
}

operator fun IntList.component2(): Int {
    return when (this) {
        IntList.Nil -> 0
        is IntList.Cons -> tail.component1()
    }
}

operator fun IntList.component3(): Int {
    return when (this) {
        IntList.Nil -> 0
        is IntList.Cons -> tail.component2()
    }
}

fun initListOf(vararg arg: Int): IntList {
    return when (arg.size) {
        /**
         * slice 获取 range内的数据 返回List
         *
         *  '*'可以将array分割成一个一个  传给可变长参数
         */
        0 -> IntList.Nil
        else -> IntList.Cons(arg[0], initListOf(*(arg.slice(1 until arg.size).toIntArray())))
    }
}

//    递归列表实现
//[0,1,2,3]
fun main() {

//    手动初始化
    val cons = IntList.Cons(0, IntList.Cons(1, IntList.Cons(2, IntList.Cons(3, IntList.Nil))))

    println(cons.toString())

//    使用initListOf  初始化
    val conss = initListOf(0, 1, 2, 3)

    println(conss.toString())

    println(conss.intJoinToString('+'))

    println("sum:${conss.sum()}")

    val (a, s, d) = conss

    println("a:$a,s:$s,d:$d")


}

你可能感兴趣的:(kotlin,my,kotlin,android,java,开发语言)