kotlin

Kotlin笔记


起步

前言

​ Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,被称之为 Android 世界的Swift,由 JetBrains 设计开发并开源。
​ Kotlin 可以编译成Java字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。
​ 在Google I/O 2017中,Google 宣布 Kotlin 成为 Android 官方开发语言。
​ 一些工具网站:
​ 在线编辑器:play.kotlinlang.org

第一个kotlin程序

​ Kotlin 程序文件以 .kt 结尾,如:hello.kt 、app.kt。

package hello                      //  可选的包头
 
fun main(args: Array<String>) {    // 包级可见的函数,接受一个字符串数组作为参数
   println("Hello World!")         // 分号可以省略
}

//类的声明
class Greeter(val name: String) {
   fun greet() { 
      println("Hello, $name")
   }
}
 
fun main(args: Array<String>) {
   Greeter("World!").greet()          // 创建一个对象不用 new 关键字
}

程序入口:
​ Kotlin 应用程序的入口点是 main 函数

fun main() {
    println("Hello world!")
}

main 的另一种形式接受可变数量的 String 参数:

fun main(args: Array<String>) {
    println(args.contentToString())
}

包的定义及导入

​ 目录与包的结构无需匹配:源代码可以在文件系统的任意位置
​ 包的声明应处于源文件顶部

package top.littled

import kotlin.text.*

基础语法

变量

​ 可变变量定义:var 关键字
​ 不可变变量定义:val 关键字,只能赋值一次的变量(类似Java中final修饰的变量)

val a: Int = 5 //常量
var x: Int = 5 //变量
//Kotlin 支持类型推断,并自动识别已声明变量的数据类型,在变量名称后可省略 type
val x = 5

基本数据类型

kotlin中的类型

//Kotlin 的基本数值类型包括 Byte、Short、Int、Long、Float、Double 等。

//不同于 Java 的是,字符不属于数值类型,是一个独立的数据类型。

//整数类型
	Byte: 8 位,范围从 -128 到 127。
	Short: 16 位,范围从 -32,768 到 32,767。
	Int: 32 位,范围从 -2^31 到 2^31 - 1。
	Long: 64 位,范围从 -2^63 到 2^63 - 1。
//浮点数类型
	Float: 32 位,单精度,带有 6-7 位有效数字。
	Double: 64 位,双精度,带有 15-16 位有效数字。
//字符类型
	Char: 16 位的 Unicode 字符。
//布尔类型
	Boolean: 有两个值:true 和 false。
//字符串类型
	String: 一系列字符的序列。
//数组类型
	Kotlin 提供了数组类型来存储同种类型的元素,例如:
	IntArray: 存储 Int 类型的数组。
	DoubleArray: 存储 Double 类型的数组。
	Array<T>: 泛型数组,可以存储任意类型。

​ 例:

fun main() {
    // 整数类型
    val byteValue: Byte = 127
    val shortValue: Short = 32767
    val intValue: Int = 2147483647
    val longValue: Long = 9223372036854775807L

    // 浮点数类型
    val floatValue: Float = 3.14F
    val doubleValue: Double = 3.141592653589793

    // 字符类型
    val charValue: Char = 'A'

    // 布尔类型
    val booleanValue: Boolean = true

    // 字符串类型
    val stringValue: String = "Hello, Kotlin!"

    // 数组类型
    val intArray: IntArray = intArrayOf(1, 2, 3, 4, 5)
    val doubleArray: DoubleArray = doubleArrayOf(1.1, 2.2, 3.3)
    val stringArray: Array<String> = arrayOf("Kotlin", "Java", "Python")
}

类型的比较

​ 在 Kotlin 中,比较两个数字可以使用标准的比较运算符,包括 ==!=<><=>=。这些运算符可以比较基本数据类型
​ Kotlin 在比较不同数据类型时会自动进行类型转换,向上转换
相等比较:
​ Kotlin 中 == 运算符用于结构相等性比较,即值的比较,而 === 运算符用于引用相等性比较,
​ 即对象是否是同一个实例。在比较基本数据类型时,通常使用 == 运算符

类型转换

​ 较小类型并不是较大类型的子类型,较小的类型不能隐式转换为较大的类型
​ 每种数据类型都有下面的这些方法,可以转化为其它的类型

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

字符串

字符串模版:
​ $ 表示一个变量名或者变量值
​ $varName 表示变量值
​ ${varName.fun()} 表示变量的方法返回值

var a = 1
// 模板中的简单名称:
val s1 = "a is $a" 
a = 2
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, but now is $a"

多行字符串:
​ 使用三引号 ''' 包裹
​ 内部没有转义并且可以包含换行以及任何其他字符

val text = """
    for (c in "foo")
        print(c)
"""
//如需删掉多行字符串中的前导空格,请使用 trimMargin() 函数
val text = """
|Tell me and I forget.
|Teach me and I remember.
|Involve me and I learn.
|(Benjamin Franklin)
    """.trimMargin()
//默认以竖线 | 作为边界前缀,但你可以选择其他字符并作为参数传入,比如 trimMargin(">")。

条件控制

if

fun main(args: Array<String>) {
    var x = 0
    if(x>0){
        println("x 大于 0")
    }else if(x==0){
        println("x 等于 0")
    }else{
        println("x 小于 0")
    }
}

When 表达式

​ when 将它的参数和所有的分支条件顺序比较,直到某个分支满足条件
​ when 既可以被当做表达式使用也可以被当做语句使用。如果它被当做表达式,符合条件的分支的值就是整个表达式的值

when (x) {
    1 -> print("x is 1")
    is String -> print("x is String")
    !in 10..20 -> {
        print("x is outside the range")
    }
    else -> print("none of the above")
}

for循环

​ for 循环可以对任何提供迭代器(iterator)的对象进行遍历,语法如下

for (item in collection) print(item)
//可以使用区间遍历
for (i in 1..3){
    println(i) //1,2,3
}

while 与 do...while 循环

while( 布尔表达式 ) {
  //循环内容
}

do {
       //代码语句
}while(布尔表达式);

返回和跳转

Kotlin 有三种结构化跳转表达式:

  • return。默认从最直接包围它的函数或者匿名函数返回。
  • break。终止最直接包围它的循环。
  • continue。继续下一次最直接包围它的循环。

Break 和 Continue 标签
​ 在 Kotlin 中任何表达式都可以用标签(label)来标记。 标签的格式为标识符后跟 @ 符号,
​ 例如:abc@、fooBar@都是有效的标签。 要为一个表达式加标签,我们只要在其前加标签即可。

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (……) break@loop
    }
}

函数

声明

fun 函数名(参数名1: 参数类型,参数名2: 参数类型): 返回类型{
}
//例
fun sum(a: Int, b: Int): Int {
    return a + b
}
//函数体可以是表达式。其返回类型可以推断出来
fun sum(a: Int, b: Int) = a + b
//返回无意义的值的函数 使用 Unit做返回值类型:  (Unit 返回类型可以省略) (类似Java中的void)
fun printSum(a: Int, b: Int): Unit {
    println("sum of $a and $b is ${a + b}")
}

可变长参数

​ 函数的变长参数可以用 vararg 关键字进行标识

fun vars(vararg v:Int){
    for(vt in v){
        print(vt)
    }
}

// 测试
fun main(args: Array<String>) {
    vars(1,2,3,4,5)  // 输出12345
}

匿名函数

  • 匿名函数的特点是可以明确指定其返回值类型。
  • 它和常规函数的定义几乎相似。他们的区别在于,匿名函数没有函数名。
            fun test(x : Int , y : Int) : Int{                  fun(x : Int , y : Int) : Int{
常规函数:      return x + y                        匿名函数:      return x + y
            }                                                   }
//匿名函数可赋值给变量(kotlin中万物皆对象)
val test3 = fun(x : Int , y : Int) : Int{
    return x + y
}

Lambda

语法:

// 无参数的情况 :
val/var 变量名 = { 操作的代码 }

// 有参数的情况
val/var 变量名 : (参数的类型,参数类型,...) -> 返回值类型 = {参数1,参数2,... -> 操作参数的代码 }

//可等价于
// 此种写法:即表达式的返回值类型会根据操作的代码自推导出来。
val/var 变量名 = { 参数1 : 类型,参数2 : 类型, ... -> 操作参数的代码 }

3. lambda表达式作为函数中的参数的时候
fun test(a : Int, 参数名 : (参数1 : 类型,参数2 : 类型, ... ) -> 表达式返回类型){
    ...
}
//lamdba内的参数名称可以省略
fun test(a : Int, 参数名 : (类型,类型, ... ) -> 表达式返回类型){
    ...
}
    // 源代码
    fun test(a : Int , b : Int) : Int{
        return a + b
    }
    // lambda
    val test : (Int , Int) -> Int = {a , b -> a + b}
    // 或者
    val test = {a : Int , b : Int -> a + b}
    // 调用
    test(3,5) => 结果为:8

传入函数内:

// lambda表达式作为函数中的参数的时候
// lambda
fun test(a : Int , b : (num1 : Int , num2 : Int) -> Int) : Int{
    return a + b.invoke(3,5)
}
// 调用
test(10,{ num1: Int, num2: Int ->  num1 + num2 })  // 结果为:18

it:
it是在当一个高阶函数中Lambda表达式的参数只有一个的时候可以使用it来使用此参数。it可表示为单个参数的隐式名称,是Kotlin语言约定的

 fun test(num1 : Int, bool : (Int) -> Boolean) : Int{
   return if (bool(num1)){ num1 } else 0
}

println(test(10,{it > 5}))
println(test(4,{it > 5}))

下划线_:
​ 在使用Lambda表达式的时候,可以用下划线(_)表示未使用的参数,表示不处理这个参数。

val map = mapOf("key1" to "value1","key2" to "value2","key3" to "value3")

map.forEach{
     key , value -> println("$key \t $value")
}

// 不需要key的时候
map.forEach{
    _ , value -> println("$value")
}

标签return:
​ 在kotlin中的lambda中的return是直接返回它所在的外部函数,导致外部函数都结束

fun foo() {
    ints.forEach {
        if (it == 0) return //直接结束 foo() 函数
        print(it)
    }
}
//如果我们需要从 lambda 表达式中返回,我们必须给它加标签并用以限制 return。
fun foo() {
    ints.forEach lit@ {
        if (it == 0) return@lit
        print(it)
    }
}
//或者直接@方法名
fun foo() {
    ints.forEach {
        if (it == 0) return@forEach
        print(it)
    }
}
//或者,我们用一个匿名函数替代 lambda 表达式。 匿名函数内部的 return 语句将从该匿名函数自身返回
fun foo() {
    ints.forEach(fun(value: Int) {
        if (value == 0) return
        print(value)
    })
}

高阶函数

​ 根据Kotlin中的约定,即当函数中只有一个函数作为参数,并且使用了lambda表达式作为相应的参数,则可以省略函数的小括号()

str.sumBy({ it: 类型 ->  it.toInt } )
//可省略为
str.sumBy{ it.toInt } 

容器

定义类

​ 使用 class 关键字定义类:

class Runoob {  // 类名为 Runoob
    // 大括号内是类体构成
}
//定义成员函数及成员变量
class Runoob() {
    var name: String = "qwq"  //kotlin中的成员变量需要初始化值,若不初始化需要lateinit关键字修饰
    fun foo() { print("Foo") } // 成员函数
}

//kotlin中类内的成员和java一样,需要创建对象后才能访问
Runoob().name //获取name值
//kotlin中没有new 关键字,创建对象直接 对象名() 即可
val object1 = Runoob()

类内的成员变量的getter 和 setter:
​ 属性声明的完整语法:
​ kotlin提供了 field关键字,在 get和set中代表该变量本身

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]
//使用例子
class Person {
    var lastName: String = "zhang"
        get() = field.toUpperCase()   // 将变量赋值后转换为大写
        set
    var no: Int = 100
        get() = field                // 后端变量
        set(value) {
            if (value < 10) {       // 如果传入的值小于 10 返回该值
                field = value
            } else {
                field = -1         // 如果传入的值大于等于 10 返回 -1
            }
        }
    var heiht: Float = 145.4f
        private set //私有化
}

// 测试 , 在调用成员变量时,若声明了get和set方法,都是通过声明的get和set方法操作值
fun main(args: Array<String>) {
    var person: Person = Person()
    person.lastName = "wang"
    println("lastName:${person.lastName}")
    person.no = 9
    println("no:${person.no}")
}

构造器

主构造器:
​ Kotlin 中的类可以有一个 主构造器,以及一个或多个次构造器,主构造器是类头部的一部分,位于类名称之后
​ 主构造器内的参数会自动声明为该对象的成员变量,在对象创建时被赋值

class Person constructor(firstName: String) {}
//如果主构造器没有任何注解,也没有任何可见度修饰符,那么constructor关键字可以省略
class Person(firstName: String) {
}
//主构造器中不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀
class Person constructor(firstName: String, age: Int) {
    var firstName: String
	var age = age //若是主构造器的参数可以直接在成员变量区域给他赋值
    init { //像java那样对成员变量赋值
        this.firstName = firstName
        println("FirstName is $firstName")
    }
}
//可以在构造器参数处直接声明成员变量
class Person(var firstName: String) {
}

次构造器:
​ 类也可以有二级构造函数,需要加前缀 constructor:

class Person { 
    constructor(parent: Person) { //传入的参数同主构造器一样,需要自行赋值给成员变量
    }
}
//如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字
class Person(val name: String) {
    constructor (name: String, age:Int) : this(name) {
        // 初始化...
    }
}

修饰符

​ 类修饰符

abstract    // 抽象类
final       // 类不可继承,默认属性,
enum        // 枚举类
open        // 类可继承,类默认是final的
annotation  // 注解类

​ 限制修饰符

限制修饰符 Kotlin中含义 与Java比较
默认(final) 不允许被继承或重写 与Java指定final的效果一致
open 允许被继承或重写 相当于Java的默认情况
abstract 抽象类或抽象方法 与Java一致
sealed 若要继承则需要将子类定义在同一个文件

​ 可见性修饰符

可见性修饰符 Kotlin中含义 与Java比较Java访问权限修饰词
public Kotlin中默认修饰符 全局可见 与Java中public效果相同
internal 模块内可见
protected 受保护修饰符,类及子类可见 含义一致,但作用域除了类及子类外,包内也可见
private 私有修饰符类内修饰只有本类可见,类外修饰文件类可见 私有修饰类,只有类内可见

继承

​ 如果一个类要被继承,可以使用 open 关键字进行修饰

open class Base          // 定义基类

class Derived : Base{} //在类声明后加: 继承的类即可继承

​ 如果子类有主构造函数, 则基类必须在主构造函数中立即初始化

open class Person(var name : String, var age : Int){// 基类

}

class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {

}

​ 子类没有主构造函数,则必须在每一个二级构造函数中用 super 关键字初始化基类

class Student : Person {

    constructor(ctx: Context) : super(ctx) {
    } 

    constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {
    }
}

重写:
​ 使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它

/**用户基类**/
open class Person{
    open fun study(){       // 允许子类重写
        println("我毕业了")
    }
}

/**子类继承 Person 类**/
class Student : Person() {

    override fun study(){    // 重写方法
        println("我在读大学")
    }
}

接口

​ Kotlin 接口与 Java 8 类似,使用 interface 关键字定义接口,允许方法有默认实现

interface MyInterface {
    fun bar()    // 未实现
    fun foo() {  //已实现
      // 可选的方法体
      println("foo")
    }
}
//实现接口 一个类或者对象可以实现一个或多个接口。多个接口间用,隔开
class Child : MyInterface,MyInterface2 {
    override fun bar() {
        // 方法体
    }
}

重写:
​ 接口中的属性只能是抽象的,不允许初始化值,接口不会保存属性值,实现接口时,必须重写属性:
​ 继承多个接口时,若遇到同名方法,可用super<父类名> 泛型的形式指定调用的方法

interface A {
    fun foo() { print("A") }   // 已实现
    fun bar()                  // 未实现,没有方法体,是抽象的
}
 
interface B {
    fun foo() { print("B") }   // 已实现
    fun bar() { print("bar") } // 已实现
}
 
class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }
 
    override fun bar() {
        super<B>.bar()
    }
}

抽象类,嵌套类,内部类,匿名内部类

抽象类:
​ 类本身,或类中的部分成员,都可以声明为abstract的

open class Base {
    open fun f() {}
}

abstract class Derived : Base() {
    override abstract fun f()
}

嵌套类:

class Outer {                  // 外部类
    private val bar: Int = 1
    class Nested {             // 嵌套类
        fun foo() = 2
    }
}
fun main(args: Array<String>) {
    val demo = Outer.Nested().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性
    println(demo)    // == 2
}

内部类:
​ 内部类使用 inner 关键字来表示。
​ 内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。

class Outer {
    private val bar: Int = 1
    var v = "成员属性"
    /**嵌套内部类**/
    inner class Inner {
        fun foo() = bar  // 访问外部类成员
        fun innerTest() {
            var o = this@Outer //获取外部类的对象
            println("内部类可以引用外部类的成员,例如:" + o.v)
        }
    }
}

fun main(args: Array<String>) {
    val demo = Outer().Inner().foo()
    println(demo) //   1
}

匿名内部类:
​ 匿名内部类就是一个没有声明名称的单例类

class Test {
    fun setInterFace(test: TestInterFace) {
        test.test()
    }
}

/**
 * 定义接口
 */
interface TestInterFace {
    fun test()
}

fun main(args: Array<String>) {
    var test = Test()
    /**
     * 采用对象表达式来创建接口对象,即匿名内部类的实例。
     */
    test.setInterFace(object : TestInterFace {
        override fun test() {
            println("对象表达式创建匿名内部类的实例")
        }
    })
}

泛型

​ 与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼

声明泛型类

class Box<T>(t: T) {
    var value = t
}
val box: Box<Int> = Box<Int>(1)
// 或者
val box = Box(1) // 编译器会进行类型推断,1 类型 Int,所以编译器知道我们说的是 Box<Int>。

泛型函数

​ Kotlin 泛型函数的声明与 Java 相同,类型参数要放在函数名的前面

fun <T> boxIn(value: T) = Box(value)

// 以下都是合法语句
val box4 = boxIn<Int>(1)
val box5 = boxIn(1)     // 编译器会进行类型推断

泛型约束

​ 我们可以使用泛型约束来设定一个给定参数允许使用的类型。
​ Kotlin 中使用 : 对泛型的类型上限进行约束

fun <T : Comparable<T>> sort(list: List<T>) {
    // ……
}
sort(listOf(1, 2, 3)) // OK。Int 是 Comparable<Int> 的子类型
sort(listOf(HashMap<Int, String>())) // 错误:HashMap<Int, String> 不是 Comparable<HashMap<Int, String>> 的子类型

​ 对于多个上界约束条件,可以用 where 子句:

fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
    where T : CharSequence,
          T : Comparable<T> {
    return list.filter { it > threshold }.map { it.toString() }
}

型变、协变、逆变:
​ 不变:有继承关系的两个类,在变成泛型类型时不再有关系。
​ 如:MathBook是Book的子类,而BaseBook与BaseBook就没关系了,是不同对象。
​ 协变:如果想让BaseBook与BaseBook继续有父子关系,即想继续支持协变,
在Java中使用? extends E表示;Kotlin中使用out E,表示上界是E。那么BaseBook继续是BaseBook的子类。
​ 逆变:与协变相反,有继承关系的两个类,在逆变之后,关系反过来了。Java中使用? super E,Kotlin中使用in E,表示下界为E。

//协变
interface Source<out T> {
    fun nextT(): T
}

fun demo(strs: Source<String>) {
    val objects: Source<Any> = strs // 这个没问题,因为 T 是一个 out-参数
    // ……
}
//逆变
interface Comparable<in T> {
    operator fun compareTo(other: T): Int
}

fun demo(x: Comparable<Number>) {
    x.compareTo(1.0) // 1.0 拥有类型 Double,它是 Number 的子类型
    // 因此,可以将 x 赋给类型为 Comparable <Double> 的变量
    val y: Comparable<Double> = x // OK!
}

星号投射

​ 相当于java中的 <?> 通配符

Kotlin的特殊机制

Null检查机制

​ Kotlin的空安全设计对于声明可为空的参数,在使用时要进行空判断处理,有两种处理方式,
​ 字段后加!!像Java一样抛出空异常,另一种字段后加?可不做处理返回值为 null 或配合 ?: 做空判断处理

//类型后面加?表示可为空
var age: String? = "23" 
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1

​ 当一个引用可能为 null 值时, 对应的类型声明必须明确地标记为可为 null。
​ 当 str 中的字符串内容不是一个整数时, 返回 null:

fun parseInt(str: String): Int? {
    return str.toIntOrNull()
}

fun printProduct(arg1: String, arg2: String) {
    val x = parseInt(arg1)
    val y = parseInt(arg2)
    
    // ...
    if (x == null) {
        println("Wrong number format in arg1: '$arg1'")
        return
    }
    if (y == null) {
        println("Wrong number format in arg2: '$arg2'")
        return
    }

    // 在进行过 null 值检查之后, x 和 y 的类型会被自动转换为非 null 变量
    println(x * y)
}

类型检测及自动类型转换

​ 我们可以使用 is 运算符检测一个对象是否是指定类型的实例(类似于 Java 中的 instanceof 关键字)

fun main() {
    val obj: Any = "Hello, Kotlin"
	//当 is 检测通过时,Kotlin 会自动将 obj 视为指定类型,因此在 if 语句的分支内不需要显式地进行类型转换。这被称为智能类型转换
    if (obj is String) {
        println("字符串长度: ${obj.length}") // 在这里 `obj` 已被智能转换为 `String`
    } else {
        println("不是字符串类型")
    }
}

区间

​ 区间表达式由具有操作符形式 .. 的 rangeTo 函数辅以 in 和 !in 形成

for (i in 1..4) print(i) // 输出“1234”

for (i in 4..1) print(i) // 什么都不输出

if (i in 1..10) { // 等同于 1 <= i && i <= 10
    println(i)
}

// 使用 step 指定步长
for (i in 1..4 step 2) print(i) // 输出“13”

for (i in 4 downTo 1 step 2) print(i) // 输出“42”

// 使用 until 函数排除结束元素
for (i in 1 until 10) {   // i in [1, 10) 排除了 10
     println(i)
}

静态成员

​ 关于静态变量、常量、函数(方法)在Kotlin的使用,主要有Object、companion object、顶层方法、 @JvmStatic注解、@JvmField注解等方式
​ kotlin中对类内的任何成员访问都是需要创建实例后才能访问,若要直接访问需要设置为静态

单列Object

​ 该方式所修饰的类,可以看做静态类,内部方法均为静态方法(定制性弱了一点)

object KtUtil {
    fun doSomething() {
        println("2023希望我们都更好,加油")
    }
}

KtUtil.doSomething() //可直接调用

伴生 companion object

​ 使用 companion object 包裹方法,只有在该作用域内的方法方为静态方法

class KtUtil {
    fun doAnything() {
        println("普通方法,不支持类名.调用")
    }
    companion object {
        fun doSomething() {
            println("2023希望我们都更好,加油")
        }}}
KtUtil.doSomething() //直接调用

顶层类 顶层方法

​ 顶层方法是指那些没有定义在任何类中的方法,Kotlin编译器会将所有顶层方法编译成静态方法
​ 该方式调用静态方法时无需通过 类名.方法,可直接调用方法
​ 变量也可以,同理 顶层变量

package com.example.ktobject

fun doSomething() {
    println("2023希望我们都更好,加油")
}

//在其他kt文件中可直接调用(若在不同包,会引入包名+方法名)
import com.example.ktobject.doSomething
 doSomething() 

注解 @JvmStatic、@JvmField

​ 我们使用 @JvmStatic 注解静态方法,使用@JvmField 注解静态参数(变量、常量)
​ 原始 object 以及 companion object方式并 不支持Java通过静态方式调用Kt静态方法,需要在方法上加 @JvmStatic 才能支持调用

package com.example.ktobject

object KtUtil {
 	@JvmField
    var anyBoy:String ="2023希望我们都更好,加油"
    @JvmStatic
    fun doSomething() {
        println("2023希望我们都更好,加油")
    }
    companion object {
        @JvmStatic
        fun doSomething() {
            println("2023希望我们都更好,加油")
        }
    }
}

对象表达式

​ Kotlin 用对象表达式和对象声明来实现创建一个对某个类做了轻微改动的类的对象,且不需要去声明一个新的子类

单例类

​ 对象表达式是基于单例类的
​ 单例类使用object 声明,声明后就创建了实例,并且只有一个实例
​ 单例内的内容直接使用单例类的名称调用即可,就像静态类一样

object DataProviderManager {
    fun registerDataProvider(provider: DataProvider) {
        // ……
    }

    val allDataProviders: Collection<DataProvider>
        get() = // ……
}
//单例类可以继承其他类
object DefaultListener : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
        // ……
    }

    override fun mouseEntered(e: MouseEvent) {
        // ……
    }
}
//如果超类型有一个构造函数,则必须传递参数给它。多个超类型和接口可以用逗号分隔
open class A(x: Int) {
    public open val y: Int = x
}

interface B {……}

val ab: A = object : A(1), B {
    override val y = 15
}

对象表达式

​ 通过对象表达式实现一个匿名内部类的对象用于方法的参数中//匿名内部类就是一个没有声明名称的单例类

window.addMouseListener(object : MouseAdapter { 	//创建一个继承于MouseAdapter类的单例类
    override fun mouseClicked(e: MouseEvent) {
        // ...
    }
    override fun mouseEntered(e: MouseEvent) {
        // ...
    }
})
posted @ 2025-06-14 00:05  LittleD-  阅读(56)  评论(0)    收藏  举报