val与var

val: 不可变变量,赋值以后不能修改,相当于java中的final
var: 可变变量

变量与类型

kotlin中一切皆对象

val a = 100
val b : Int = 100

kotlin中支持类型推导,也就是相当于

val int = 100 //推导为Int类型
val long = 1234567L //必须加L后缀才会推导成Long类型
val double = 13.14 //推导为Double类型
val float = 13.14F //必须加F后缀才会推导成Float类型
val hex = 0xAF //使用“0x”,来代表十六进制字面量
val binary = 0b01010101 //使用“0b”前缀,来代表十六进制字面量

在kotlin中,无法像java那样,可以强制类型转换,他是像下面这样,这也体现了一切皆对象的思想

val a = 1
val i: Double = a.toDouble()

kotlin中提供了一个字符串模板:

val in = "world"
val out = "Hello $in !"
// 输出结果:Hello World!

同时Kotlin中还可以用一个变量简单的标识多行文字

val a = """
        这是第一行
        这是第二行
        这是第三行 """
val a = arrayof("1","2")

kotlin中声明一个列表要使用listof(),但是这个列表无法添加新的元素,要使用mutableListOf()
声明的列表才可以进行添加元素。我认为这和kotlin中的推荐有关——“编程中尽量多使用不变元素

val a = listof("1","2")
a.add("3") //此时会报错
val b = mutableListOf("1","2")
b.add("3") //此时不会报错

空安全

在Kotlin中,处理可能为空的值时,需要使用一些特殊的操作符和注解。

  • kotlin中所有类型默认都是非空的,在声明的时候,如果他可能为空,就需要在类型后面加一个 ?
var str: String? = "Hello" // 声明一个可空的字符串类型
  • 安全调用操作符(Safe Call Operator): 如果左边的表达式不为空,它会执行调用的方法,否则返回null。
var str: String? = "Hello"
println(str?.length) // 如果str不为空,则打印长度,否则打印null
  • Elvis操作符: 如果左边的表达式不为空,它会执行调用的方法,否则返回右边的表达式的值。
var str: String? = "Hello"
println(str?.length ?: 0) // 如果str不为空,则打印长度,否则打印0
  • 非空断言操作符(Non-null Assertion Operator): 告诉编译器该变量一定不为空,如果为空会抛出异常。
var str: String? = "Hello"
println(str!!.length) // 如果str为空,会抛出KotlinNullPointerException
  • 安全转换操作符(Safe Casts Operator): 在继承关系中安全地将对象转换为子类型。
val any: Any = "Hello"
val str = any as? String
println(str?.length) // 如果转换成功,则打印长度,否则打印null

object关键字

在kotlin中,object关键字,却有三种迥然不同的语义,分别可以定义:匿名内部类、单例模式、伴生对象。Kotlin的设计者认为,这三种语义本质上都是 在定义一个类的同时还创建了对象,所以将其统一成一个关键字:object

什么是匿名内部类?

  • 匿名内部类(Anonymous Inner Class)在 Java 中是一种特殊的内部类,它没有名字,可以在代码中临时创建,并且通常用于实现接口或继承类的实例。

Java中的匿名内部类:

public interface OnClickListener {
    void onClick(View v);
}

image.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        gotoPreview();
    }
}); 

这就是典型的匿名内部类的写法,View.OnClickListener是一个接口,因此我们在创建它的时候,必须 实现它内部没有实现的方法

同样的,在kotlin中,也有匿名内部类的说法,但是java中是使用 new来实现一个匿名内部类,而kotlin中则是使用 object关键字,来新建匿名内部类

image.setOnClickListener(object: View.OnClickListener {
    override fun onClick(v: View?) {
        gotoPreview()
    }
})

Java和Kotlin相同的地方就在于,它们的接口与抽象类,都不能直接创建实例。想要创建接口和抽象类的实例,我们必须通过匿名内部类的方式。

不过,在Kotlin中,匿名内部类还有一个特殊之处,就是我们在使用object定义匿名内部类的时候,其实还可以 在继承一个抽象类的同时,来实现多个接口

interface A {
    fun funA()
}

interface B {
    fun funB()
}

abstract class Man {
    abstract fun findMan()
}

fun main() {
    // 这个匿名内部类,在继承了Man类的同时,还实现了A、B两个接口
    val item = object : Man(), A, B{
        override fun funA() {
            // do something
        }
        override fun funB() {
            // do something
        }
        override fun findMan() {
            // do something
        }
    }
}

在日常的开发工作当中,我们有时会遇到这种情况:我们需要继承某个类,同时还要实现某些接口,为了达到这个目的,我们不得不定义一个内部类,然后给它取个名字。但这样的类,往往只会被用一次就再也没有其他作用了。

在 Java 中,类是单继承的,这意味着一个类只能继承一个父类。如果我们想要在继承某个类的同时实现接口(尤其是多个接口),通常要:

  1. 创建一个新的类,继承目标类;
  2. 在新类中实现需要的接口;
  3. 这个新类仅用于特定场景,且没有通用性,可能用完这一次就再也不用了。

为了满足这些需求,我们不得不给这个新类取个名字,即使它只会使用一次。这样做不仅代码显得冗余,还可能让类文件变得杂乱。例如:

// 需要继承SomeClass,同时实现SomeInterface
class MyTempClass extends SomeClass implements SomeInterface {
    @Override
    public void someMethod() {
        // 实现逻辑
    }
}

如果在代码中,这个 MyTempClass 仅用在一个特定的场景里,以后也不会再复用,为它命名和定义一个类就显得有些多余。

在Kotlin中,通过 object,可以同时继承类并实现接口,不用额外创建新类,也不需要命名。我们可以在用到的地方直接创建它。

val instance = object : SomeClass(), SomeInterface {
    override fun someMethod() {
        // 实现逻辑
    }
}

在这段代码中:

  1. object 表达式直接创建了一个继承 SomeClass 并实现 SomeInterface 的匿名对象。
  2. 不需要命名或创建新类文件。
  3. 仅在需要的地方创建和使用,用完即抛。

这个对象没有名字,也不需要额外定义一个类文件,在代码中它是匿名的,且可以直接继承和实现想要的父类或接口。这样做避免了冗余类的定义,让代码更加简洁,不必再为一次性用的类命名或管理。

Java单例模式可以查看文章Java设计模式学习笔记