kotlin05.类/属性/单例/伴生类

185 阅读4分钟

属性与field关键字

  • kotlin为每个class的变量自动生成get和set方法
  • 可以通过自定义get()/set(value)来覆盖自动生成的方法
  • 在get/set方法中通过field关键字来获取定义属性的值
  • var 转化为 private
  • val 转化为 private final
class Test{
    var name ="abc"
        get() = field.capitalize()
        set(value)  {
            field = value + "a"
        }
}

//转化后的代码
public final class Test {
   @NotNull
   private String name = "abc";

   @NotNull
   public final String getName() {
      return StringsKt.capitalize(this.name);
   }

   public final void setName(@NotNull String value) {
      Intrinsics.checkNotNullParameter(value, "value");
      this.name = value + "a";
   }
}

构造函数

  • 通过(var param) 来定义属性
  • 当我们新建对象时,会将属性同步初始化
class PlayB(var name:String){}
var p = PlayB("hello")

//等价于
public final class PlayB {
   @NotNull
   private String name;

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

   public final void setName(@NotNull String var1) {
      Intrinsics.checkNotNullParameter(var1, "<set-?>");
      this.name = var1;
   }

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

普通变量

普通变量只使用一次,通常用下划线开头_

class PlayB(_name:String){}
var p = PlayB("hello") // p无法访问_name,,它仅是一个临时变量

//等价于
public final class PlayB {
   public PlayB(@NotNull String _name) {
      Intrinsics.checkNotNullParameter(_name, "_name");
      super();
   }
}

多个构造函数

  • 次构造函数中,不允许使用var定义属性
  • 可以提供默认参数
class Player(var name:String , var age:Int = 10 , var sex:Boolean){

    constructor(name: String):this(name ,100 ,false)
    constructor(name: String , age: Int):this(name ,age ,false){
        this.name = name.toUpperCase()
    }
}

初始化代码块 init

构造函数执行完毕后,执行初始化代码块,一般用于变量或属性的初始化,初始化代码块实际上会被添加到构造方法中顺序执行(请注意顺序二字)

class Player(var name:String , var _age:Int , var sex:Boolean){
    constructor(name: String):this(name ,100 ,false)
    constructor(name: String , age: Int):this(name ,age ,false){
        this.name = name.toUpperCase()
    }

    init {
        println("do something initial $p")
    }
    val p:String = "father"
}

//输出
/kotlinStudy/src/ClassTest.kt:18:40
Kotlin: Variable 'p' must be initialized

//等价于 , 此处的p由于尚未初始化是获取不到的.
public Player(@NotNull String name, int _age, boolean sex) {
  Intrinsics.checkNotNullParameter(name, "name");
  super();
  this.name = name;
  this._age = _age;
  this.sex = sex;
  String var4 = "do something initial " + this.p;
  boolean var5 = false;
  System.out.println(var4);
  this.p = "father";
}

初始化顺序

  • 主构造函数申明的属性
  • 类级别的属性
  • init代码块中的属性
  • 次构造函数中的属性 image.png

延迟初始化lateinit

::wapeon.isInitialized表示调用初始化检查函数,它是从lateinit的扩展

class PlayerB{
    lateinit var wapeon: String

    fun ready(){
        wapeon = "gun"
    }

    fun battle(){
        if (::wapeon.isInitialized) println(" ready to battle")
    }
}

var pb = PlayerB()
pb.ready()
pb.battle()

惰性初始化 懒汉加载

  • val config by lazy {xxx}
  • 在首次使用的时候执行初始化操作,仅初始化一次
  • 不能被设置其它值,因此不能采用var来定义属性,必须是val
class PlayerC{
    val config:String by lazy { loadConfig() }

    private fun loadConfig():String{
        println("load config ")
        return "config"
    }
}

//如果用var来定义变量,会提示以下错误
Type 'Lazy<TypeVariable(T)>' has no method 'setValue(PlayerC, KProperty<*>, String)' and thus it cannot serve as a delegate for var (read-write property)

继承

  • 默认情况下class不可被继承,只有申明为open的class才可被继承
  • 类中的方法也需要申明为open才可以被重写,默认情况下方法是public final类型
  • 判断对象类型可以用 is
  • 所有的类默认继承自 Any超类
open class PlayerC(name:String){
    val config:String by lazy { loadConfig() }

     open fun loadConfig():String{
        println("load config ")
        return "config"
    }
}

class Child(name:String) : PlayerC(name) {
    override fun loadConfig():String{
        return "child config"
    }
}

public open class Any {
    public open operator fun equals(other: Any?): Boolean
    public open fun hashCode(): Int
    public open fun toString(): String
}

object 关键字

构造单例对象

  • 申明这样的对象时,不需要加class修饰
  • 它不能提供构造函数
  • 是一个线程安全的单例,类的初始化锁解决线程竞争问题
  • static包含的代码会在Application被加载的时候执行
object Application{

}

//等价于
public final class Application {
   @NotNull
   public static final Application INSTANCE;

   private Application() {
   }

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

构造匿名内部类对象

用于创建一个匿名内部类对象,如java中的各种listener

open class Application{
    init {
        println("init application")
    }

    open fun doSomething(){
        println("do some thing")
    }
}

 var App = object : Application() {
        init {
            println("init App")
        }

        override fun doSomething() {
            println("doSomething in App")
        }
    }

App.doSomething()
App.doSomething()

//转化出来的代码
App = new Application() {
     public void doSomething() {
        String var1 = "doSomething in App";
        boolean var2 = false;
        System.out.println(var1);
     }
    
     {
        String var1 = "init App";
        boolean var2 = false;
        System.out.println(var1);
     }
};

//输出
init application
init App
doSomething in App
doSomething in App

构造伴生对象

  • 静态内部单例
  • companion object 构造一个伴生对象
  • 生成了一个命名为Application.Companion的静态内部类
  • 一个类只能有一个伴生对象,因为默认生成的对象和类会重名
  • 生成了一个静态对象 public static final Application.Companion Companion
  • 它适用于初始化一些静态代码块,仅会被初始化一次.
  • class Internal也回产生一个静态内部类,它与companion object的区别在于,它不会产生静态对象.
open class Application{
    companion object {
        val config:String="config"
        init {
            println("do some initial")
        }

        fun loadConfig() :String= "load something"
    }

    class Internal{
        fun loadConfig() :String= "load Internal"
    }
}

println( Application.loadConfig())
println( Application.loadConfig())

//输出
do some initial
load something
load something

//转化后的java代码
public class Application {
   @NotNull
   public static final Application.Companion Companion = new Application.Companion((DefaultConstructorMarker)null);

   public static final class Internal {
      @NotNull
      public final String loadConfig() {
         return "load Internal";
      }
   }

   public static final class Companion {
      @NotNull
      public final String loadConfig() {
         return "load something";
      }
      
       @NotNull
      public final String getConfig() {
         return Application.config;
      }

      public final void setConfig(@NotNull String var1) {
         Intrinsics.checkNotNullParameter(var1, "<set-?>");
         Application.config = var1;
      }

      private Companion() {
      }

      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

静态内部类

在类中定义的其它类默认都是静态内部类,如Internal是一个 public static final的静态内部类

open class Application{

    var appid:String = "1233"

    init {
        println("init application")
    }

    open fun doSomething(){
        println("do some thing")
    }

    companion object {
        fun loadConfig() :String= "load something"
    }

    class Internal{
       fun internalFun(){
            
        }
    }
}

//转化后的代码
public static final class Internal {
}

data 数据类

  • 用来存储数据
  • 重新实现了toString,hashCode和equals方法
  • 增加了copy函数的定义,可以修改某个属性
  • == 比较的是引用 .普通对象比较的是引用,也就是必须hashCode相等并且equals方法返回true才表示相等,而data对象通过实现自定义的hashCode和equals方法,使得只要值相等,==就返回true
data class Point(var x:Int , var y:Int){}
var p1 = Point(10 , 20)
var p2 = Point(10 , 20)

var p3 = p2.copy(5)

println(p1 == p2) //true
println(p1) // Point(x=10, y=20)
println(p3) // Point(x=5, y=20)

//等价于
public final class Point {
   private int x;
   private int y;

   public final int getX() {
      return this.x;
   }

   public final void setX(int var1) {
      this.x = var1;
   }

   public final int getY() {
      return this.y;
   }

   public final void setY(int var1) {
      this.y = var1;
   }

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }

   public final int component1() {
      return this.x;
   }

   public final int component2() {
      return this.y;
   }

   @NotNull
   public final Point copy(int x, int y) {
      return new Point(x, y);
   }

   @NotNull
   public String toString() {
      return "Point(x=" + this.x + ", y=" + this.y + ")";
   }

   public int hashCode() {
      return Integer.hashCode(this.x) * 31 + Integer.hashCode(this.y);
   }

   public boolean equals(@Nullable Object var1) {
      if (this != var1) {
         if (var1 instanceof Point) {
            Point var2 = (Point)var1;
            if (this.x == var2.x && this.y == var2.y) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }
}

解构语法支持

通过申明若干个组件函数component1,component2,....,每个组件函数负责返回一个属性值.数据类自动做了解构组件函数申明

普通对象支持解构

image.png

data对象解构自动支持

var p1 = Point(10 , 20)
var (x , y) = p1

data数据类使用条件

  • 必须包含至少一个带参数的主构造函数
  • 构造参数类型必须是val 或者var
  • class不能是abstract , open , sealed 和 inner修饰

Enum枚举

可以传递参数,定义函数.

enum class EnumTest(var x :Int , var y:Int) {
    dir_0(10 , 20),
    dir_1(100 , 200),
    dir_2(1000 , 2000)
}

fun main() {
    var e = EnumTest.dir_0
    println(e is EnumTest) // true
    println("${e.x} ${e.y}") // 10 20
}

运算符重载

image.png

data class Point(var x:Int , var y:Int){

    operator fun plus(point: Point) : Point{
       return Point(x + point.x , y + point.y)
    }
    //或者
    operator fun minus(point: Point) = Point(x - point.x , y - point.y)
}

fun main() {
    var p1 = Point(10 , 20)
    var p2 = Point(20 , 40)

    println(p1 + p2) // Point(x=30, y=60)
    println(p1 - p2) // Point(x=-10, y=-20)
}

密封类 sealed class

等价于抽象内部类,外部无法实例化.

sealed class sealClass {
    object sealA:sealClass()
    object sealB:sealClass()
}

//等价于
public abstract class sealClass {
   private sealClass() {
   }

   public static final class sealA extends sealClass {
      @NotNull
      public static final sealClass.sealA INSTANCE;

      private sealA() {
         super((DefaultConstructorMarker)null);
      }

      static {
         sealClass.sealA var0 = new sealClass.sealA();
         INSTANCE = var0;
      }
   }

   
   public static final class sealB extends sealClass {
      @NotNull
      public static final sealClass.sealB INSTANCE;

      private sealB() {
         super((DefaultConstructorMarker)null);
      }

      static {
         sealClass.sealB var0 = new sealClass.sealB();
         INSTANCE = var0;
      }
   }
}

它解决了枚举类型的限制(枚举类型必须一致,比如都是Int/String)

sealed class Color {
    class Red(val value: Int) : Color()
    class Green(val value: Int) : Color()
    class Blue(val name: String) : Color()
}

fun isInstance(color: Color) {
    when (color) {
        is Color.Red -> TODO()
        is Color.Green -> TODO()
        is Color.Blue -> TODO()
    }
}

sealed class 一些用法

不同参数的操作

sealed class UiOp {
    object Show: UiOp()
    object Hide: UiOp()
    class TranslateX(val px: Float): UiOp()
    class TranslateY(val px: Float): UiOp()
}

fun execute(view: View, op: UiOp) = when (op) {
    UiOp.Show -> view.visibility = View.VISIBLE
    UiOp.Hide -> view.visibility = View.GONE
    is UiOp.TranslateX -> view.translationX = op.px
    is UiOp.TranslateY -> view.translationY = op.px
}