专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Kotlin学习笔记

安装方式

37_1.png

  • 使用方法
  • scoop install kotlin 等待完成就完成部署了,连环境变量都可以不用修改了

内置类型

基本类型

类型 大小(比特数) 最小值 最大值
Byte 8 -128 127
Short 16 -32768 32767
Int 32 -2,147,483,648 (-231) 2,147,483,647 (231 – 1)
Long 64 -9,223,372,036,854,775,808 (-263) 9,223,372,036,854,775,807 (263 – 1)
  • long形容数字的话,末尾必须是大写L
  • 无法默认类型转化,int的变量无法直接赋值给long 必须显示的调用 val f = e.toLong()
  • 所有类型和java的基础类型差不多,就是首字母是大写的而已,Int Double Float String Byte这类
  • 有无符号类型,UInt,在上面的基础类型前面加上 U
  • 字符串可以使用 $ 来添加 "value of String j is = $j" 如果使用对象函数调用的话使用大括号括起来"length of String j is:${j.length}" println("Range of UInt [${UInt.MIN_VALUE}, ${UInt.MAX_VALUE}]")
  • 不需要 new 了
  • === 对比的是引用;== 相当于 java的 equals 函数
  • Row String 的引用
val n = """
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <h1>网站标题</h1>
    </body>
    </html>
""".trimIndent()

trimIndent 这个函数主要是去除公共空格

<!-- 这前面的四个空格或者tab就是公共空格 --><!DOCTYPE html>
    <html lang="en">
    <head>

  • valvar 的区别是
    • val 修饰的变量类似于java变量前面加上 final
    • var修饰的变量有自动类型推导功能,可以使用ide显示出来类型
  • kotlin定义变量的方式是:var 变量名: 变量类型 = 变量值 但是这种方式可以使用 var 变量名 = 变量值 来代替
  • 如果需要声明一个空的变量, var 变量名: 变量类型? 就可以

数字

  • Long 末尾使用 L
类型 大小(比特数) 有效数字比特数 指数比特数 十进制位数
Float 32 24 8 6-7
Double 64 53 11 15-16
  • Float 末尾使用 f 或者 F 标记一个数字
  • 小数点出现默认类型为 Double
  • 十六进制 0x0F
  • 二进制 0b00001011
  • 不支持八进制

数字字面值中的下划线

val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

  • == equals 方法比较, === 引用类型比较
  • ?操作符只有前面的 a不为null时才可以计算后面的方法 a?.length
  • !! 符号表示判断该变量是否为 null, 为 null 这抛出异常kotlin.KotlinNullPointerException
  • is 或者 !is 是否为某个类型, 经过 is 判断如果成功, 变量就会变成is之后的类型
  • Any 就相当于 java 中的 Object
val a: Int = 10000
println(a === a)
// 使用 类型? 就有装箱的情况
val boxedA: Int? = a
val anotherBoxedA: Int? = a
println("boxedA = $boxedA")
println("anotherBoxedA = $anotherBoxedA")
println(boxedA === anotherBoxedA) // 引用比较 false
println(boxedA == anotherBoxedA) // equals 方法比较 true

val str = 10000
val str1: Int? = str
val str2: Int? = str
println(str1 == str2) // equals 方法比较 true
println(str1 === str2) // 引用比较 false

if (str is String) {
    // 如果 str 原本是 Any 类型, 如果使用了 is 关键字则隐含着表示这个对象转化成 String 类型
    return str.toUpperCase()
}

  • as` 不安全类型转化

val x: String = y as String // 这里 如果y 为空就会抛出异常, 则无法转化成String val x: String? = y as String? // 这样 如果y为空就不会转化成String, 但是它如果无法转化会抛出异常 安全的可空转化操作方式 val x: String? = y as? String // 失败返回 null 这里 x 时可空类型, 不会抛出异常

val a:Any = 100
val b1 = a as? String
//    val b2 = a as String? // 抛出异常
println("b1 = $b1") // 类型 b1 ===> String?
//    println("b2 = $b2") // 类型 b2 ===> String?

  • kotlin 已经一般的隐式转换需要调用方法转化

toByte(): Byte toShort(): Short toInt(): Int toLong(): Long toFloat(): Float toDouble(): Double toChar(): Char

kotlin在计算中的隐式转化

// 这种隐式转化也是可以的
val c: Int = 10
val d: Byte = 3
val e: Int = c + d
println("e = $e")

运算

整数运算

整数除法

val x = 5 / 2
println(x == 2) // true

val y = 5L / 2
println(y == 2L) // true

val z = 5 / 2.toDouble()
println("z val is $z") // 2.5
println(z == 2.5) // true

  • 位运算
// 位运算
val x = (1 shl 2) // 左移 2 位
println("x = $x")
val y = x and 0x000FF000
println("y = $y")

这是完整的位运算列表(只用于 IntLong):

shl(bits) – 有符号左移 shr(bits) – 有符号右移 ushr(bits) – 无符号右移 and(bits) – 位与 or(bits) – 位或 xor(bits) – 位异或 inv() – 位非

  • 浮点数比较

相等性检测:a == ba != b 比较操作符:a < ba > ba <= ba >= b 区间实例以及区间检测:a..bx in a..bx !in a..b

  • 字符需要注意 \$ 这个用法
  • Elvis语句 ( 如果空, 则走另一个 )

val res = 表达式1 :? 表达式2 如果表达式1的结果是null, 则执行表达式2的语句, 结果(res)就是表达式的结果了

    val map = mapOf("1" to "2", "3" to "4", "5" to "6")
    val get = map["2"] ?: map["5"] // 这里 map["2"] 为空, 则走 map["5"] 最终返回 6
    println("map[2] = $get")

kotlin 包名可以和``public`类对象不同

  • kotlin中的包和java 不一样, 不需要包在哪,. 程序的包名就叫什么 kotlin 包名: package com.zhazha2121212 实际的类在 com.zhazha 下 但是这个类编译后的路径在: build\classes\kotlin\main\com\zhazha2121212

37_2.png

  • 给包取别名 import com.zhazha.other.multiply as myMultiply
/**
 *
 * Create by zhazha on 2020-05-23
 */
fun multiply(a: Int, b: Int) = a * b

// other file
import com.zhazha.other.multiply
import com.zhazha.other.multiply as myMultiply

private fun func03Import() {
    println(multiply(10, 5))
    println(myMultiply(10, 6))
}

逻辑操作符

if 使用方法

  • kotlin中没有三目运算符, 但是可以使用 if expression res1 else res2
private fun maxMinFuncUp() {
    var x: Double = 2444.0
    var y: Double = 25555.0
    var max: Double = if (x > y) x else y
    var min: Double = if (x > y) y else x
    println("max = $max, min = $min")
}

private fun maxMinFunc02() {
    var x: Long = 1000L
    var y: Long = 200L
    var max: Long = if (x > y) {
        println("max = $x")
        x
    } else {
        println("min = $y")
        y
    }
    var min: Long = if (x > y) {
        println("min = $y")
        y
    } else {
        println("max = $x")
        x
    }
    println("max = $max, min = $$min")
}
private fun maxMinFunc01() {
    var x: Int = 10
    var y: Int = 20
    var max: Int = 0
    var min: Int = 0
    if (x > y) {
        max = x
        min = y
    } else {
        min = x
        max = y
    }
    println("max = $max, min = $min")
}

  • if 表达式使用, if 可以直接赋值

val max = if (a > b) a else b

/**
 * if 表达式
 */
fun ifExpression(a: Int, b: Int): Int {
    return if (a > b) a else b
}

  • ★注意下面这种用法, 在末尾添加直接写上变量, 则表示返回值
/**
 * if 表达式
 */
fun ifExpression2(a: Int, b: Int): Int {
    val max = if (a > b) {
        println("max is a , a = $a")
        a
    } else {
        println("max is b, b = $b")
        b
    }
    return max
}

数组

37_3.png

kotlin的数组也是包含基础和装箱类型 单纯的使用 Array<Int> 这种就是装箱方案

使用 IntArray 或者 CharArray 等都是基础方案

/**
 * 数组的创建
 */
println("数组创建:")
val array = IntArray(5) { it + 1 }
val array1 = IntArray(5, init = { it + 1 })
println(array.contentToString())
println(array1.contentToString())

val arr = intArrayOf(1, 2, 3, 4, 5)
println(arr.contentToString())

/**
 * 数组的长度
 */
println("数组长度:")
val size = IntArray(5).size
println("数组长度是:$size")

/**
 * 数组的读写
 */
val strArr = arrayOf("hello", "world")
println(strArr.contentToString())
strArr.forEach { s -> println(s) }
strArr[1] = "Kotlin"
println("${strArr[0]}, ${strArr[1]}")

/**
 * 数组遍历
 */
val floats = floatArrayOf(1f, 3f, 5f, 7f)
for (fl in floats) {
    println(fl)
}

floats.forEach { fl -> println(fl) }

/**
 * 数组包含关系
 * 判断数组内部是否存在值未 1f 的元素
 */
if (1f in floats) {
    println("1f exists in variable 'floats' ")
}

/**
 * 不包含
 */
if (20f !in floats) {
    println("20f not exists in variable 'floats'")
}

区间

  • 1 .. N [1, N]
  • N downTo 1 [N, N – 1, .. , 1]
  • 1 until N [1, N)
  • 1 .. 8 step 2 // 步长
    /**
     * 闭区间
     * 区间从小到大
     */
    val intRange = 1..10 // [1, 10]
    val charRange = 'a'..'z' // ['a', 'z']
    val longRange = 1L..100L // [1L, 100L]
    // intRange.forEach(Consumer { t -> println(t) })
    // intRange.forEach { t -> print("$t ") }
    println(intRange.joinToString())

    /**
     * 先开后闭区间
     * 区间值从小到大
     */
    val intRangeExclusive = 1 until 10 // [1, 10)
    val charRangeExclusive = 'a' until 'z' // ['a', 'z')
    val longRangeExclusive = 1L until 100L // [1L, 100L)
    // intRangeExclusive.forEach { t: Int? -> print("$t ") }
    println(intRangeExclusive.joinToString())

    /**
     * 反向迭代闭区间
     * 区间值从大到小
     */
    val intRangeReverse = 10 downTo 1 // [10, 9, .. , 1]
    val charRangeReverse = 'z' downTo 'a' // a ~ z
    val longRangeReverse = 100L downTo 1L // 1L ~ 100L
    // intRangeReverse.forEach { t: Int? -> print("$t ") }
    println(intRangeReverse.joinToString())

    /**
     * 区间步长
     */
    val intRangeWithStep = 1..10 step 2
    val charRangeWithStep = 'a'..'z' step 2
    val longRangeWithStep = 1L..100L step 2
    // intRangeWithStep.forEach { t: Int? -> print("$t ") }
    println(intRangeWithStep.joinToString())

    /**
     * 区间的应用
     */
    println("区间的应用: ")
    val arrayOf = intArrayOf(1, 3, 5, 7)
    // for (i in 0 until arrayOf.size) {
    //     println(arrayOf[i])
    // }
    for (i in arrayOf.indices) {
        println(arrayOf[i])
    }

list.indices 集合区间 –> 0..array.size - 1

遍历数组的方法

    val m = intArrayOf(1, 2, 3)
    // m.set(1, 4)
    m[1] = 5

    // for (v in m) {
    //     println("v = $v")
    // }

    // 迭代器
    // val iterator = m.iterator()
    // while (iterator.hasNext()) {
    //     println(iterator.nextInt())
    // }

    // lmabda
    // m.forEach { println(it)  }

    // 直接使用数组的方法
    // println(m.contentToString())

    // 返回前N个元素, 不包括index那个元素 [0, index)
    // println(m.take(2))
    println(m.joinToString())

  • 数组 IntArray是java的int[] 其遍历方法是
// 定义并初始化一个数组
val arrays = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
for (array in arrays) {
    println(array)
}
println("---------------------")
arrays.forEach { println(it) }
println("---------------------")
for (index in arrays.indices) {
    println("array[$index] = ${arrays[index]}")
}
println("---------------------")
for ((index, value) in arrays.withIndex()) {
    println("index = $index, array[index] = ${arrays[index]}, value = $value")
}

集合

  • 复用 java API
  • 提供丰富易用方法
  • 运算符级别的支持, 简化集合框架的访问

37_4.png

typealias别名

typealias ArrayList<E> = java.util.ArrayList<E> 该类和java类相同

直接使用操作符对集合进行操作

可以用 -= += [] 对集合进行操作 Map 类型可以直接利用数组完成 map['HashMap'] = 1

Map集合遍历隐含的类 Pair 和 Triple

  • pair

val pair = "Hello" to "World" val pair2 = Pair("Hello", "World") first = pair.first

fun main() {
    val x = "aaa"
    val y = "2"

    println(printProduct(x, y))

    val hashMap = hashMapOf("1" to 1, "2" to 2, "3" to 3)
//    val a = hashMap.get("4")
    val a = hashMap["4"] // a == null
    val res = a?.plus(1) // null 加上了 ? 之后任何计算都是 null???
    println(res) // null
    if (null == res) {
        println("res is null")
    }

//    res!! // kotlin.KotlinNullPointerException

}

fun parseInt(str: String) = str.toIntOrNull()

fun printProduct(str1: String, str2: String): Int? {
    val num1 = parseInt(str1)
    val num2 = parseInt(str2)
    return num1?.plus(num2!!)
}

  • Triple则表示三个参数时使用

字符串模板

val i = 10
println("i = $i") // // prints "i = 10"

调用函数的话

val s = "string"
println("$s.length is ${s.length}")

如果 $ 需要被打印出来

val price = """
${'$'}9.99
"""

kotlin 类

kotlin构造方法

kotlin 的类存在两种构造方法

    • primary 构造方法: 这种构造方法只能有一个
      • secondary 构造方法: 这种构造方法可以有多个或者没有
class EmptyClass
// 这种在类旁边写上初始化的方式, 就是 primary 构造方法
class MyClass constructor(username: String) {
    private var username: String = username.toUpperCase()
    init {
        println(this.username)
        // 这个就相当于初始化代码块
        this.username = username
        println(this.username)
    }
}

fun main() {
    val myClass = MyClass("zhazha")
    println(myClass)
}

  • 构造方法
// 空类是可以存在的
class EmptyClass

class MyClass constructor(username: String) {
    private var username: String = username.toUpperCase()

    // secondary构造函数需要主动调用 primary 构造函数, 或者调用另一个调用了primay构造方法的secondary构造方法
    // constructor() {
        constructor() : this("") {
        println("无参数构造函数")
    }
    // 类都存在一个 init 方法, 在调用 primary 构造方法时才会使用到
    init {
        println(this.username)
        // 这个就相当于初始化代码块
        this.username = username
        println(this.username)
    }
}

fun main() {
    val myClass = MyClass("zhazha")
    println(myClass)
    // secondary构造函数需要主动调用 primary 构造函数
    val myClass1 = MyClass()
    println(myClass1)
}

class Student10(username: String, age: Int, money: Double) {
    var username: String
    var age: Int
    var money: Double
    init {
        this.username = username
        this.age = age
        this.money = money
    }
}

class Student10_1(username: String, age: Int, money: Double) {
    var username: String
    // 设置了属性 username 不可 set (赋值功能)
        private set
    var age: Int
        private set
    var money: Double
        private set

    init {
        this.username = username
        this.age = age
        this.money = money
    }

}

class Person constructor(username: String) {
    private var username: String
    private var age: Int
    private var address: String
    private var money: Double

    init {
        this.username = username
        this.age = 20
        this.address = "beijing"
        this.money = 10_0000.0
    }

    constructor(age: Int) : this("") {
        this.age = age
    }

    constructor(money: Double) : this("") {
        this.money = money
    }

    constructor(username: String, money: Double) : this(username) {
        this.money = money
    }

    constructor(username: String, age: Int) : this(username) {
        this.age = age
    }

    constructor(username: String, age: Int, address: String) : this(username, age) {
        this.address = address
    }

    fun printInfo() {
        println("username = ${this.username}, age = ${this.age}, address = ${this.address}, money = ${this.money}")
    }
}

fun main() {
    val student10 = Student10("zhazha", 12, 100_0000.0)
    println(student10)
    val student101 = Student10_1("1111", 11, 11111111.0)
    println(student101)
    println(student101.money)
    println(student101.username)
    println(student101.age)
    val person0 = Person("zhazha")
    val person1 = Person(10)
    val person2 = Person(1000_0000.0)
    val person3 = Person("zhazha", 1000_0000.0)
    val person4 = Person("zhazha", 20)
    val person5 = Person("zhazha", 7, "beijing")
    person0.printInfo()
    person1.printInfo()
    person2.printInfo()
    person3.printInfo()
    person4.printInfo()
    person5.printInfo()
}

/**
 * 在类旁边使用了 private var username: String 字段就相当于在类内部添加了个属性
 */
class Student(private var username: String, private var age: Int, private var address: String) {
    fun printInfo() {
        println("username = $username, age = $age, address = $address")
    }
}

/**
 * 如果构造方法有注释或者关键字, 构造方法关键字则不能省略
 */
class Student2 private constructor(username: String)

/**
 * 如果primary构造方法的所有参数都有默认值, 那么这个kotlin就会为这个类添加一个无参数构造函数
 * 这里的 username 默认是 public 关键字
 */
class Student3(var username: String = "zhazha")

fun main() {
    val student = Student("heihei", 22, "beijing")
    student.printInfo()
    var student3 = Student3()
    println(student3.username)
    student3 = Student3("gggggggg") // 这里可以赋值
    println(student3.username)
}

类的继承

字段继承和方法继承

open class Person1(name: String, age: Int)

// 类似于c++的初始化成员列表
class Child(name: String, age: Int) : Person1(name, age)

open class Person2(name: String)

class Child2 : Person2 {
    // super 调用父类的构造方法
    constructor(name: String) : super(name)
}

fun main() {
    val person1 = Person1("zhazha", 111)
    println(person1)
    val child = Child("heihei", 12)
    println(child)
    val person2 = Person2("xixi")
    println(person2)
    val child2 = Child2("gun")
    println(child2)
}

继承遇到函数相同的情况下便是重写

open class Fruit {
    open fun print() {
        println("fruit")
    }

    fun expirationData() {
        println("1 day")
    }
}

class Apple : Fruit() {
    override fun print() {
        super.print()
        println("apple")
    }
}

class Orange : Fruit() {
    // 防止Orange的之类发生重写
    override fun print() {
        super.print()
        println("Orange")
    }
}

fun main() {
    val apple = Apple()
    println("apple = $apple")
    apple.expirationData()
    val orange = Orange()
    println(orange)
}

字段重写以及set/get方法权限的设置

open class MyParent {
    open val name: String = "parent"
}

// val 修饰的字段可以被重写成var字段
class MyChild : MyParent() {
    override var name: String = "child"
}

class MyChild2(override val name: String = "mychild2") : MyParent()

fun main() {
    val myChild = MyChild()
    println(myChild)
    val myChild2 = MyChild2()
    println(myChild2)
    val myParent2 = MyParent2("zhazh2")
    println(myParent2)
    val myChild02 = MyChild02("zhazhaChild")
    println(myChild02)
}

open class MyParent2(open var name: String = "MyParent2") {
    open fun method() {
        println("parent method")
    }
}

/**
 * 父 val 字段  子类可以为 var, 反之不行
 */
class MyChild02(name: String) : MyParent2(name) {
    override fun method() {
        super.method()
        println("MyChild02")
    }

    override var name: String = ""
        get() = super.name + "MyChild02"
        set(value) {
            field = value
        }
}

属性的get/set方法和属性真实field

class ThePerson(address: String, name: String) {
    val age: Int
        get() = 20
    var address: String = address
        get() {
            println("get() address")
            return field // 其内部真实的字段是 field
        }
        set(value) {
            println("set() address")
            field = value // 其内部真实的字段是 field
        }

    // name 字段默认提供 get/set 方法. 成为属性
    var name: String = name
        private set // 设置 set 方法不被外界调用
}

fun main() {
    // 对 kotlin 属性的修改和读取都是使用的 get/set 方法
    val thePerson = ThePerson("shanghai", "zhangsan")
    println(thePerson.age)
    println(thePerson.address)
    thePerson.address = "beijing"
    println(thePerson.address)
    println(thePerson.name)
    // thePerson.name = "ddddddddddddddd" // 这个方法被 private set
}

接口和多继承, 遇到父类相同方法实现

interface ParentA {
    fun method()
    fun method2() {
        println("com.zhazha.ParentA.method2: 父类方法存在自己的方法体, 所以子类不需要重写")
    }

    fun method3() {
        println("com.zhazha.ParentA.method3: 父类方法 A 存在两个相同方法")
    }

    fun method4();
}

open class ParentC {
    open fun method3() {
        println("com.zhazha.ParentC.method3: 父类方法 C 存在两个相同的方法")
    }
    open fun method4() {
        println("com.zhazha.ParentC.method4: 如果类ParentA的method4方法没实现, 那么他会调用哪个方法???")
    }
}

/**
 * 类 B 多实现 方法 method3 必须明确指出是哪个父类的方法, 使用 super<A> 或者 super<B> 这两个方法一起实现
 */
class ChildB : ParentA, ParentC() {
    override fun method() {
        println("com.zhazha.ChildB.method: 如果接口的方法没有方法体, 则子类需要实现方法")
    }

    override fun method2() {
        super.method2()
        println("com.zhazha.ParentA.method2: 即使父类存在方法体的方法实现, 子类还是可以重写这个方法")
    }

    override fun method3() {
        super<ParentA>.method3()
        super<ParentC>.method3()
        println("com.zhazha.ChildB.method3: 子类同时调用了父类和子类的这个method3方法")
    }

    override fun method4() {
        super.method4()
        println("子类只能使用实现了method4方法类的方法")
    }
}

fun main() {
    val b = ChildB()
    b.method()
    b.method2()
    b.method3()
    b.method4()
}

对象类和伴生对象

半生类其实类似于java的静态方法和字段

/**
 * 这个是无法用在单例上的
 */
object MyObject {
    fun method() {
        println("这是一个对象类. 可能也是一个单例哦")
    }
}

class MyTest16 {
    // 伴生对象只有一个
    // companion object MyCompanion {
    // }

    // 伴生对象名字可以省略, 如果省略不写, 则默认名字是 Companion
    // companion object MyCompanion {
        companion object {
        private var a: Int = 100
        var b: Int = 1000
        fun method() {
            println("companion object method")
        }

        @JvmStatic
        fun methodStatic() {
            println("static func method")
            methodPrivate()
        }

        // 这个方法是外面无法调用的
        private fun methodPrivate() {
            println("this is private method ")
        }
    }

}

fun main() {
    // 测试对象类是否为单例
    // checkSingleton()
    MyTest16.method()
    MyTest16.methodStatic()
    MyTest16.Companion.method()
    MyTest16.Companion.methodStatic()
    println(MyTest16.b)
}

/**
 * 对象类的单例检测是不可以的
 */
private fun checkSingleton() {
    val clazz = MyObject.javaClass
    val constructor = clazz.getDeclaredConstructor()
    if (constructor.trySetAccessible()) {
        val instance = constructor.newInstance()
        if (instance == MyObject) {
            println("true 0")
        } else if (instance === MyObject) {
            println("true 1")
        } else {
            println("但不是单例")
        }
        instance.method()
    }
    MyObject.method()
}

字段的延迟加载

// 主构造函数参数上不允许使用“ lateinit”修饰符
class TheClass(val age: Int = 1000) {
    // 延迟加载属性, 这个lateinit关键字好像不支持基础类型
    lateinit var username: String

    init {
        println("真正初始化方法被调用")
    }

    fun init() {
        this.username = "zhangsan"
        println("用户定义的init方法")
    }

    fun print() {
        println(this.username)
    }
}

fun main() {
    val theClass = TheClass(1)
    println(theClass.age)
    theClass.init()
    theClass.print()
    println(theClass.username)
}

lateinit不能用于基础类型, 不能用于带?的类型, 不能定义 set/get 方法

  • 扩展方法(帮类添加一个扩展方法)
    • 不修改真实类的方法
    • 无多态, 纯静态操作
    • 结果只看声明类型, 不看实际类型
open class A

class B : A()

fun A.a() = "a"
fun B.a() = "b"

fun printW(a: A) {
    println(a.a())
}

fun main() {
    // 扩展方法它是静态的, 没有多态
    // 不会添加扩展方法到类中
    // 没有多态, 所以结果看声明类型, 不看动态类型(实际类型)
    printW(B()) // 打印是 a
}

文章永久链接:https://tech.souyunku.com/41858

未经允许不得转载:搜云库技术团队 » Kotlin学习笔记

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们