《Kotlin入门实战》CH4 | 类与面向对象编程

字数 708阅读 25

类与面向对象编程

从面向过程到面向对象,再到设计模式,架构设计,面向服务,Sass、Pass和Iass等思想,各种软
件理论思想五花八门,但万变不离其宗:

你要解决一个什么样的问题?
你的问题是哪个领域的?
你的模型(数据结构)是什么?
你的算法是什么?
你对这个世界的本质认知是怎样的?
你的业务领域的逻辑问题、流程是什么?

声明类

1. 空类

2. 声明类和构造函数

class People(var id: Int) {
    var age: Int = 0;           // 没有延迟初始化的必须声明的时候初始化
    lateinit var name: String;  // 延迟初始化

    // 二级构造器必须调用主构造器
    // 构造器中参数不能使用val或者var声明
    constructor(id: Int, age: Int, name: String) : this(id) {
        this.age = age
        this.name = name
    }

    // 重写toString方法
    override fun toString(): String {
        return "People(id=$id, age=$age, name='$name')"
    }
}


fun main() {
    val people = People(42119999)   // 调用主构造器
    people.age = 20                 // 类中属性默认是public,在其他包中也可以访问
    people.name = "kris"
}

当子类继承了某个类之后,便可以使用父类中的成员变量,但并不是完全继承父类的所有成员变量。具体的原则如下:

  • 能够继承父类的public和protected成员变量;
  • 不能继承父类的private成员变量;
  • 对于父类的包访问权限成员变量,如果子类和父类在同一个包下,则子类能够继承;否则,子类不能继承;
  • 对于子类可以继承的父类成员变量,如果在子类中出现了同名称的成员变量,则会发生隐藏现象,即子类的成员变量会屏蔽掉父类的同名成员变量。如果要在子类中访问父类中的同名成员变量,需要使用super关键字进行引用。

3 Object对象

Kotlin中没有静态对象,需要实现单例模式,可以使用Object单例对象

object User{
    var id = "admin"
    var pw = "admin"

    override fun toString(): String {
        return "$id + $pw"
    }
}

fun main() {
    println(User) //  admin + admin
}

或者使用伴生对象

class DBClient(var id : Int){
    fun open(){
        println("DBClient id $id")
        openDB()
    }

    // 伴生对象,一个类只有一个
    companion object DBEngine{
        fun openDB(){
            println("DB engine open db")
        }
    }
}

fun main() {
    DBClient(1).open()
    DBClient(2).open()
}
/*
DBClient id 1
DB engine open db
DBClient id 2
DB engine open db
*/

4. 数据类

//主构造器不能为空
data class Music(var id : Int){
    lateinit var name : String;
}

这是Kotlin的数据类,可以反编译一下,查看对应的Java代码

public final class Music {
   @NotNull
   public String name; // 在类中声明的变量是public的
   private int id; // 主构造器中变量式私有的,也就是说不能继承
   
   // 构造器 
   public Music(int id) {
      this.id = id;
   }
    
// ============= get and set fun
   // get&set 都是被声明为final,不能重写
   @NotNull
   public final String getName() {
      String var10000 = this.name;
      if (var10000 == null) {
         Intrinsics.throwUninitializedPropertyAccessException("name");
      }

      return var10000;
   }

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

   public final void setId(int var1) {
      this.id = var1;
   }
//===============

   // component1 返回主构造器中下表为1的属性值
   public final int component1() {
      return this.id;
   }
   
   // 拷贝函数
   @NotNull
   public final Music copy(int id) {
      return new Music(id);
   }

   // $FF: synthetic method
   @NotNull
   public static Music copy$default(Music var0, int var1, int var2, Object var3) {
      if ((var2 & 1) != 0) {
         var1 = var0.id;
      }

      return var0.copy(var1);
   }
   // 只包含主构造器中的域
   @NotNull
   public String toString() {
      return "Music(id=" + this.id + ")";
   }
   // 只包含主构造器中的域
   public int hashCode() {
      return this.id;
   }
   // 只包含主构造器中的域
   public boolean equals(@Nullable Object var1) {
      if (this != var1) {
         if (var1 instanceof Music) {
            Music var2 = (Music)var1;
            if (this.id == var2.id) {
               return true;
            }
         }

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

  • 主构造函数至少包含一个参数;
  • 参数必须标识为val或者var;
  • 不能为abstract、open、sealed或者inner;
  • 不能继承其他类(但可以实现接口)。
  • 可以在解构声明中使用

Pair

Kotlin中提供了二元组Pair,和三元组Triple,可以使用Pair实现Map

public data class Pair<out A, out B>(
    public val first: A,
    public val second: B
) : Serializable {
    public override fun toString(): String = "($first, $second)"
}

/**
 * Creates a tuple of type [Pair] from this and [that].
 *
 * This can be useful for creating [Map] literals with less noise, for example:
 * @sample samples.collections.Maps.Instantiation.mapFromPairs
 */
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
// 重写了中缀表达式to

使用Pair实现Map

fun main() {
    val map = mapOf(1 to 'A', 2 to 'B', 3 to 'C')
    println(map) // 创建的是LinkedHashMap类型的Map
}

// mapOf方法在 kotlin.collections包中(默认导入)
public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V> =
    if (pairs.size > 0) pairs.toMap(LinkedHashMap(mapCapacity(pairs.size))) else emptyMap()

内部类

1.普通内部类

一个类可以嵌套其他类中,并且可以嵌套多层,普通内部类不持有外部类的引用,所以不能访问外部类属性。

class Out{
    var v = 1
    class inner{
        fun cout(){
            println(v) // Error
        }
    }
}

2.嵌套内部类

如果需要在内部类中获取外部类的this指针,可以使用嵌套内部类

class Out{
    var v = 1

    inner class inner{
        fun cout(){
            println(v) // OK
        }
    }
}

3.匿名内部类

匿名内部类也可以访问外部类属性。匿名内部类最经典的运用场景应该就是创建子线程

class MyThread {
    // 匿名内部类实现
    fun doRun1() {
        Thread(object : Runnable {
            override fun run() {
                println(Thread.currentThread().id)
            }
        }).start()
    }

    // Lambda表达式实现
    fun doRun2() {
        Thread(Runnable {
            println(Thread.currentThread().id)
        }).start()
    }

    //使用lambda表达式和匿名内部类
    fun doRun3() {
        val run = Runnable {
            println(Thread.currentThread().id)
        }
        Thread(run).start()
    }

    // 直接使用lambda表达式
    fun doRun4() {
        val run = {
            println(Thread.currentThread().id)
        }
        Thread(run).start()
    }

}


fun main() {
    println(Thread.currentThread().id)  // 1
    MyThread().doRun1()                 // 12
    MyThread().doRun2()                 // 13
    MyThread().doRun3()                 // 14
    MyThread().doRun4()                 // 15
}

方法4和方法2其实是一样的。方法1和方法3其实是一样的。

推荐阅读更多精彩内容