Bootstrap

【Android】Kotlin教程(5)

1.继承

默认情况下,Kotlin 类是最终(final)的——它们不能被继承。 要使一个类可继承,请用 open 关键字标记它:

open class Base(p: Int)

class Derived(p: Int) : Base(p)
open class product(val name : String) {

    open fun description() = "Product: $name"

    fun load() = "nothing.."
}

class LuxuryProduct(name: String) : product(name) {
    override fun description() = "Luxury Product: $name"
}

fun main() {
    val p = product("Book")
    println(p.description())
    println(p.load())
    val lp = LuxuryProduct("Gold")
    println(lp.description())
    println(lp.load())
}

2.检查类型

Kotlin中is运算符可以用来检查某个对象的类型。

    println(lp is product) // true
    println(lp is LuxuryProduct) // true
    println(p is LuxuryProduct) // false

3.类型转换

    if(p is LuxuryProduct){
        println((p as LuxuryProduct).special())
    }

4.Any超类

在 Kotlin 中所有类都有一个共同的超类 Any,Any 有三个方法:equals()hashCode() toString()。因此,为所有 Kotlin 类都定义了这些方法。

5.object关键字

  • 使用object关键字,可以定义一个只能产生一个实例的类-单例
  • 使用object关键字有三种方式:对象声明、对象表达式、伴生对象
  1. 单例模式
    object 可以用来声明一个单例对象。这种对象在整个程序中只有一个实例,并且可以包含属性和方法。
object Singleton {
    val prop: String = "I'm a singleton property"
    
    fun doSomething() {
        println("Doing something in the singleton")
    }
}

fun main() {
    // 直接通过名称访问单例
    println(Singleton.prop)  // 输出 I'm a singleton property
    Singleton.doSomething()  // 输出 Doing something in the singleton
}

2.伴生对象(Companion Object)

object 还可以用于定义伴生对象。伴生对象是与类关联的一个单例对象,通常用于提供静态成员或工厂方法。

class MyClass {
    companion object Factory {
        fun createInstance(): MyClass {
            return MyClass()
        }

        val someProperty: String = "This is a companion object property"
    }
}

fun main() {
    val instance = MyClass.createInstance()
    println(MyClass.someProperty)  // 输出 This is a companion object property
}

在这个例子中,Factory 是 MyClass 的伴生对象。你可以直接通过类名访问伴生对象中的方法和属性。

3.对象表达式
object 表达式还可以用来创建一个临时的对象实例,这个对象可以继承自某个类或实现某个接口。

open class BaseClass(val name: String) {
    open fun sayHello() {
        println("Hello, $name")
    }
}

fun main() {
    val obj = object : BaseClass("Kotlin") {
        override fun sayHello() {
            super.sayHello()
            println("Nice to meet you!")
        }
    }

    obj.sayHello()
    // 输出:
    // Hello, Kotlin
    // Nice to meet you!
}

6.嵌套类

如果一个类只对另一个类有用,那么将其嵌入到该类中并使这两个类保持在一起是合乎逻辑的,可以使用嵌套类。

class Animal {

    class Dog(var name:String){
        fun show() = print("Dog is $name")
    }

    fun battle(){

    }
}

fun main(){
    val dog = Animal.Dog("Jack")
    dog.show()
}

7.数据类

  • 数据类是专门设计用来存储数据的类
  • 数据类提供了toString的个性化实现
  • ==符号默认情况下,比较对象就是他们的引用值,数据类提供了equalshashCode个性化实现。
data class Coordinate (var x :Int,var y:Int){
    val isInBounds = x > 0 && y > 0

}


fun main() {
    println(Coordinate(10,20))
    println(Coordinate(10,20) == Coordinate(10,20)) // true
}

反编译:

  @NotNull
   public String toString() {
      return "Coordinate(x=" + this.x + ", y=" + this.y + ')';
   }
   
   public int hashCode() {
      int result = Integer.hashCode(this.x);
      result = result * 31 + Integer.hashCode(this.y);
      return result;
   }

   public boolean equals(@Nullable Object other) {
      if (this == other) {
         return true;
      } else if (!(other instanceof Coordinate)) {
         return false;
      } else {
         Coordinate var2 = (Coordinate)other;
         if (this.x != var2.x) {
            return false;
         } else {
            return this.y == var2.y;
         }
      }
   }

8.copy函数

copy 函数是数据类(data class)的一个重要特性。它允许你创建一个现有对象的副本,并可以选择性地修改其中的一些属性。copy 函数自动生成,因此你无需手动编写它。

data class Person1(val name: String, var age: Int, val email: String)

fun main() {
    val alice = Person1("Alice", 30, "[email protected]")

    // 创建一个新对象,只更改年龄
    val olderAlice = alice.copy(age = 31)

    println(alice)  // 输出: Person(name=Alice, age=30, [email protected])
    println(olderAlice)  // 输出: Person(name=Alice, age=31, [email protected])
}

9.解构声明

自定义类中实现 componentN() 函数来支持解构声明:

class PlayerScore(val experience:Int,val level:Int){

    operator fun component1() = experience

    operator fun component2() = level
}

fun main() {
    val(experience,level) = PlayerScore(100,1)
    println("experience: $experience")
    println("level: $level")
}

对于数据类,Kotlin 会自动为每个属性生成 componentN() 函数,这使得可以直接解构数据类的实例:

data class Person1(val name: String, var age: Int, val email: String)

fun main() {
    val alice = Person1("Alice", 30, "[email protected]")

    // 创建一个新对象,只更改年龄
    val olderAlice = alice.copy(age = 31)

    println(alice)  // 输出: Person(name=Alice, age=30, [email protected])
    println(olderAlice)  // 输出: Person(name=Alice, age=31, [email protected])


    val (name, age, email) = alice
    println("Name: $name, Age: $age, Email: $email")
}

10.运算符重载

如果要将内置运算符应用在自定类身上,必须重写运算符函数,告诉编译器该如何操作自定义类。

import kotlin.math.pow
import kotlin.math.sqrt

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

    operator fun plus(other: Coordinate2) = Coordinate2(x + other.x, y + other.y)
    operator fun minus(other: Coordinate2) = Coordinate2(x - other.x, y - other.y)

    fun distanceTo(other: Coordinate2): Double {
        return sqrt((x - other.x).toDouble().pow(2) + (y - other.y).toDouble().pow(2))
    }

    override fun toString(): String {
        return "Coordinate2(x=$x, y=$y)"
    }
}

fun main() {
    val coordinate2 = Coordinate2(10, 20)
    val coordinate3 = Coordinate2(30, 40)

    val resultPlus = coordinate2 + coordinate3
    println("coordinate2 + coordinate3 = $resultPlus")  // 输出: coordinate2 + coordinate3 = Coordinate2(x=40, y=60)

    val resultMinus = coordinate2 - coordinate3
    println("coordinate2 - coordinate3 = $resultMinus")  // 输出: coordinate2 - coordinate3 = Coordinate2(x=-20, y=-20)

    val distance = coordinate2.distanceTo(coordinate3)
    println("Distance between coordinate2 and coordinate3 = $distance")  // 输出: Distance between coordinate2 and coordinate3 = 28.284271247461902
}

11.枚举类

枚举类,用来定义常量集合的一种特殊类。

enum class Direction {
    NORTH, SOUTH, EAST, WEST
}

enum class Color(val rgb: Int) {
    RED(0xFF0000),
    GREEN(0x00FF00),
    BLUE(0x0000FF)
}

enum class Direction {
    NORTH{
        override fun toString(): String {
            return "go to north"
        }
    }, SOUTH{
        override fun toString(): String {
            return "go to south"
        }
    }, EAST{
        override fun toString(): String {
            return "go to east"
        }
    }, WEST{
        override fun toString(): String {
            return "go to west"
        }
    }
}

fun main() {
    println(Direction.EAST) // go to east
}

12.代数数据类型

可以用来表示一组子类型的闭集,枚举类就是一种简单的ADT

enum class LicenceStatus {
    UNQUALIFIED,
    LEARNING,
    QUALIFIED
}


class Driver(var status : LicenceStatus){
    fun checkLicence() : String {
        return when(status){
            LicenceStatus.QUALIFIED -> "You can drive"
            LicenceStatus.LEARNING -> "You can learn to drive"
            LicenceStatus.UNQUALIFIED -> "You can not drive"
        }
    }
}

fun main() {
    val driver = Driver(LicenceStatus.UNQUALIFIED)
    println(driver.checkLicence()) // You can not drive
}

13.密封类

密封类(Sealed Class)是 Kotlin 中的一种特殊类,它用来表示受限的类层次结构。这意味着密封类的所有直接子类都必须在同一个文件中定义,或者作为密封类的嵌套类来定义。这种设计有助于确保类型安全,并且可以让你更清晰地控制继承关系。

  • 限制类层次:通过限制哪些类可以继承一个特定的基类,你可以更好地控制可能的数据类型。
  • 模式匹配:当使用 when 表达式时,编译器能够确保你已经处理了所有可能的情况,避免遗漏任何一种情况。
sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val message: String) : Result()
    object Loading : Result()
}
fun handleResult(result: Result) = when (result) {
    is Result.Success -> println("Success: ${result.data}")
    is Result.Error -> println("Error: ${result.message}")
    Result.Loading -> println("Loading...")
}

// 调用示例
handleResult(Result.Success("Data loaded"))
handleResult(Result.Error("Failed to load data"))
handleResult(Result.Loading)

在这个例子中,handleResult 函数使用 when 表达式来处理不同类型的 Result。由于 Result 是一个密封类,编译器会检查 when 表达式是否覆盖了所有可能的子类。如果遗漏了任何一个子类,编译器将报错。

优点

  • 类型安全:编译器可以确保你已经处理了所有可能的子类,从而减少了运行时错误的可能性。
  • 简洁性:使用 when 表达式时,不需要写冗长的 else 分支来处理未知情况。
  • 可维护性:当你需要添加新的子类时,只需要在一个地方进行修改,并且编译器会强制你在所有相关的 when 表达式中添加相应的处理逻辑。
;