kotlin-learn

kotlin-learn

SYuan03 Lv4

菜鸟教程:Kotlin 教程 | 菜鸟教程 (runoob.com)

官方文档:入门 · Kotlin 官方文档 中文版 (kotlincn.net)

在线运行:Kotlin Playground: Edit, Run, Share Kotlin Code Online (kotlinlang.org) / https://try.kotlinlang.org

简介

Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,被称之为 Android 世界的Swift,由 JetBrains 设计开发并开源。

Kotlin 可以编译成Java字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。

在Google I/O 2017中,Google 宣布 Kotlin 成为 Android 官方开发语言。

变量

如果你学过Java并且足够细心的话,你可能发现了Kotlin中Int的首字母是大写的,而Java中int的首字母是小写的。不要小看这一个字母大小写的差距,这表示Kotlin完全抛弃了Java中的基本数据类型,全部使用了对象数据类型。在Java中int是关键字,而在Kotlin中Int变成了一个类,它拥有自己的方法和继承结构。

为什么要设计val和var

函数

一个语法糖:当一个函数中只有一行代码时,Kotlin允许我们不必编写函数体,可以直接将唯一的一行代码写在函数定义的尾部,中间用等号连接即可。

让kotlin自己作类型推导,所以返回类型都不用写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package nju.dsy.helloworld

import kotlin.math.max

fun largerNumber(num1:Int, num2:Int): Int {
return max(num1, num2)
}

fun largerNumber2(num1: Int, num2: Int) = max(num1, num2)

fun main() {
var a: Int = largerNumber2(2, 40)
a = a * 10
println("a = " + a)
}

if条件语句

可以有返回值

Kotlin中的if语句相比于Java有一个额外的功能,它是可以有返回值的,返回值就是if语句每一个条件中最后一行代码的返回值。

1
2
3
4
5
fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) {
num1
} else {
num2
}

When条件语句

值匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun getScore(name: String) = when (name) {
"Tom" -> 86
"Jim" -> 77
"Jack" -> 95
"Lily" -> 100
else -> 0
}
fun getScore2(name: String) = when {
name.startsWith("Tom") -> 86
name == "Jim" -> 77
name == "Jack" -> 95
name == "Lily" -> 100
else -> 0
}

也支持类型匹配,使用is关键字

1
2
3
4
5
6
7
fun checkNumber(num: Number) {
when (num) {
is Int -> println("number is Int")
is Double -> println("number is Double")
else -> println("number not support")
}
}

循环语句

1
2
3
4
val range1 = 0..9       // 闭区间
val range2 = 0 until 10 // 左闭右开区间
val range3 = 0 until 10 step 2 // 左闭右开区间,步长为2
val range4 = 9 downTo 0 step 2 // 闭区间,倒序

面向对象编程

对话框在默认情况下自动选中的是创建一个File,File通常是用于编写Kotlin顶层函数和扩展函数的

继承

这就是Kotlin不同的地方,在Kotlin中任何一个非抽象类默认都是不可以被继承的,相当于Java中给类声明了final关键字。

之所以这么设计,其实和val关键字的原因是差不多的,因为类和变量一样,最好都是不可变的,而一个类允许被继承的话,它无法预知子类会如何实现,因此可能就会存在一些未知的风险。Effective Java这本书中明确提到,如果一个类不是专门为继承而设计的,那么就应该主动将它加上final声明,禁止它可以被继承。

open关键字

主动告诉编译器这个类可以被继承(即使是非抽象类

构造函数

任何一个面向对象的编程语言都会有构造函数的概念,Kotlin中也有,但是Kotlin将构造函数分成了两种:主构造函数次构造函数

你可能会问,主构造函数没有函数体,如果我想在主构造函数中编写一些逻辑,该怎么办呢?Kotlin给我们提供了一个init结构体,所有主构造函数中的逻辑都可以写在里面

父类的字段传参不用加var/val

简单来说就是子类有主构造函数的话,也就是子类名后面有()不管是否有参数,说明是有主构造函数的,那么就需要继承父类的构造函数

如果子类没有主构造函数而是有次构造函数,那么就不需要在初始化时调用父类的构造函数了

1
2
3
4
5
6
7
8
9
10
// 很乱,就看懂这个得了
// Kotlin规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)
class Student(val sno: String, val grade: Int, name: String, age: Int)
:
Person(name, age) {
constructor(name: String, age: Int) : this("", 0, name, age) {
}
constructor() : this("", 0) {
}
}

接口

与java类似,单继承,多实现

特性:默认实现

可见性修饰符

数据类:data关键字

自动重写hashCode(),toString(),equals()方法

这三个方法在Java中是Object类的常用方法,它们在Java编程中非常重要,并且在很多场景下都是关键的。下面简要介绍它们的用途和重要性:

  1. hashCode()方法:
    • 用途:hashCode()方法返回对象的哈希码值,它是一个32位整数,用于散列数据结构中,如哈希表。
    • 重要性:在使用散列数据结构时,比如HashSet、HashMap等,哈希码值决定了对象在数据结构中的存储位置。确保正确实现hashCode()方法是保证散列数据结构正常运作的关键。在集合类中查找对象时,首先通过哈希码值定位可能存在的位置,然后再使用equals()方法比较对象是否真正相等。
  2. toString()方法:
    • 用途:toString()方法返回对象的字符串表示形式,通常用于将对象转换为可读的字符串,方便调试和日志记录。
    • 重要性:在调试和日志输出中,将对象转换为可读的字符串表示形式是很常见的需求。如果没有自定义toString()方法,通常会返回默认的类名和哈希码,这对于调试和日志记录来说是不够有用的。通过重写toString()方法,可以自定义对象的输出格式,使其更加有意义。
  3. equals()方法:
    • 用途:equals()方法用于比较两个对象是否相等。在默认情况下,它比较的是对象的引用(即内存地址),但在很多情况下,我们希望比较对象的内容是否相等。
    • 重要性:在集合类中查找对象时,通常需要通过equals()方法来确定对象是否与集合中的某个元素相等。如果不正确实现equals()方法,可能导致集合类无法正确识别对象的相等性,从而引发错误的结果。通常,重写equals()方法需要同时重写hashCode()方法,以保持对象相等时哈希码值一致的规则。

总结:这三个方法在Java中很关键,特别是在涉及集合类的使用时。确保正确实现hashCode()equals()方法可以保证对象在散列数据结构中正确存储和查找,而重写toString()方法可以方便地查看对象的内容。同时,这些方法也是面向对象编程中的基本原则之一,可以帮助我们更好地设计和使用Java类。

省流:hashCode()和equals()方法在集合类的使用中有较大的作用

单例类:Object类

kotlin隐藏了实现

lambda编程

mutableListOf()函数

1
2
3
4
5
6
7
8
9
10
fun main() {
val list = mutableListOf(
"Apple", "Banana", "Orange", "Pear",
"Grape"
)
list.add("Watermelon")
for (fruit in list) {
println(fruit)
}
}

mutableSetOf()函数

mutableMapOf()函数

lambda表达式的语法结构

最原始

1
2
3
4
5
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val lambda = { fruit: String -> fruit.length }
val maxLengthFruit = list.maxBy(lambda)
// -> 后面是函数体,最后一行是lambda表达式的返回值,也就是说上例返回fruit.length
// 传入lambda表达式的参数是fruit:String

可以看到,maxBy函数实质上就是接收了一个Lambda参数而已,并且这个Lambda参数是完全按照刚才学习的表达式的语法结构来定义的,因此这段代码应该算是比较好懂的。

第一步简化

继续简化

最终

在这里,it关键字代表集合中的每个元素(在这种情况下,代表集合中的每个字符串),并且由于length是字符串的属性,编译器可以推断出it是String类型,因此无需显式指定参数类型。

集合的map函数

filter函数

any存在 all所有

返回true, false

Java函数式API的使用

Thread类的构造方法中接收了一个Runnable参数,我们可以使用如下Java代码创建并执行一个子线程:

1
2
3
4
5
6
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread is running");
}
}).start();

注意,这里使用了匿名类的写法,我们创建了一个Runnable接口的匿名类实例,并将它传给了Thread类的构造方法,最后调用Thread类的start()方法执行这个线程。

而如果直接将这段代码翻译成Kotlin版本,写法将如下所示:

1
2
3
4
5
Thread(object : Runnable {
override fun run() {
println("Thread is running")
}
}).start()

Kotlin中匿名类的写法和Java有一点区别,由于Kotlin完全舍弃了new关键字,因此创建匿名类实例的时候就不能再使用new了,而是改用了object关键字。这种写法虽然算不上复杂,但是相比于Java的匿名类写法,并没有什么简化之处。

但是别忘了,目前Thread类的构造方法是符合Java函数式API的使用条件的,下面我们就看看如何对代码进行精简,如下所示:

1
2
3
Thread(Runnable {
println("Thread is running")
}).start()

这段代码明显简化了很多,既可以实现同样的功能,又不会造成任何歧义。因为Runnable类中只有一个待实现方法,即使这里没有显式地重写run()方法,Kotlin也能自动明白Runnable后面的Lambda表达式就是要在run()方法中实现的内容。

另外,如果一个Java方法的参数列表中有且仅有一个Java单抽象方法接口参数,我们还可以将接口名进行省略,这样代码就变得更加精简了:

1
2
3
Thread({
println("Thread is running")
}).start()

不过到这里还没有结束,和之前Kotlin中函数式API的用法类似,当Lambda表达式是方法的最后一个参数时,可以将Lambda表达式移到方法括号的外面。同时,如果Lambda表达式还是方法的唯一一个参数,还可以将方法的括号省略,最终简化结果如下:

1
2
3
Thread {
println("Thread is running")
}.start()

总结就是只要没有歧义,随便怎么简化

挖坑:高阶函数

空指针检查

空指针异常检查提前

可为空的类型系统

?操作符

?.操作符

?:左边空就返回右边

let函数

  • 标题: kotlin-learn
  • 作者: SYuan03
  • 创建于 : 2023-07-24 10:26:50
  • 更新于 : 2024-03-10 20:03:47
  • 链接: https://bblog.031105.xyz/posts/2023-Summer-Courses-百度移动端/kotlin-learn.html
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论