Kotlin - 入门
# 创建项目
在 IDEA 中新建项目,选择: Kotlin (项目):
- 构建系统:Gradle
- JDK:尽可能新的
- Gradle DSL:Kotlin
# Main 入口
fun main() {
println("Hello, world!")
// Hello, world!
}
# 语法
# 变量
// 只读变量(类似于js的const)
val popcorn = 5 // There are 5 boxes of popcorn
val hotdog = 7 // There are 7 hotdogs
// 可变变量(与js的var同理)
var customers = 10 // There are 10 customers in the queue
// Some customers leave the queue
customers = 8
println(customers)
// 8
# 字符串模板
参考:字符串 | Kotlin 文档 --- Strings | Kotlin Documentation (opens new window)
val customers = 10
println("There are $customers customers")
// There are 10 customers
println("There are ${customers + 1} customers")
// There are 11 customers
# 基本类型
// 整型
val year: Int = 2020
val amount: Long = 350_000_000
// 无符号整型
val score: UInt = 100u
// 浮点数
val currentTemp: Float = 24.5f
val price: Double = 19.99
// 布尔值
val isEnabled: Boolean = true
// 字符
val separator: Char = ','
// 字符串
val message: String = "Hello, world!"
声明一个变量而不对其进行初始化:
val d: Int
# 集合
# List
// 只读列表(类似于数组)
val readOnlyShapes = listOf("triangle", "square", "circle")
println(readOnlyShapes)
// [triangle, square, circle]
// 可变列表(类似于arrayList)
val shapes: MutableList<String> = mutableListOf("triangle", "square", "circle")
println(shapes)
// [triangle, square, circle]
Casting (铸造) 将可变列表变成只读列表;下述 Set、Map 同理
val shapes: MutableList<String> = mutableListOf("triangle", "square", "circle")
val shapesLocked: List<String> = shapes
# Set
// Read-only set
val readOnlyFruit = setOf("apple", "banana", "cherry", "cherry")
// Mutable set with explicit type declaration
val fruit: MutableSet<String> = mutableSetOf("apple", "banana", "cherry", "cherry")
println(readOnlyFruit)
// [apple, banana, cherry]
# Map
// Read-only map
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println(readOnlyJuiceMenu)
// {apple=100, kiwi=190, orange=100}
// Mutable map with explicit type declaration
val juiceMenu: MutableMap<String, Int> = mutableMapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println(juiceMenu)
// {apple=100, kiwi=190, orange=100}
# 控制流
# if
val d: Int
val check = true
if (check) {
d = 1
} else {
d = 2
}
println(d)
// 1
# when (类 switch)
val obj = "Hello"
when (obj) {
// Checks whether obj equals to "1"
"1" -> println("One")
// Checks whether obj equals to "Hello"
"Hello" -> println("Greeting")
// Default statement
else -> println("Unknown")
}
// Greeting
val obj = "Hello"
val result = when (obj) {
// If obj equals "1", sets result to "one"
"1" -> "One"
// If obj equals "Hello", sets result to "Greeting"
"Hello" -> "Greeting"
// Sets result to "Unknown" if no previous condition is satisfied
else -> "Unknown"
}
println(result)
// Greeting
fun main() {
val trafficLightState = "Red" // This can be "Green", "Yellow", or "Red"
val trafficAction = when (trafficLightState) {
"Green" -> "Go"
"Yellow" -> "Slow down"
"Red" -> "Stop"
else -> "Malfunction"
}
println(trafficAction)
// Stop
}
更多 when 语法参考文档
# for
for (number in 1..5) {
// number is the iterator and 1..5 is the range
print(number)
}
// 12345
val cakes = listOf("carrot", "cheese", "chocolate")
for (cake in cakes) {
println("Yummy, it's a $cake cake!")
}
// Yummy, it's a carrot cake!
// Yummy, it's a cheese cake!
// Yummy, it's a chocolate cake!
# While
var cakesEaten = 0
while (cakesEaten < 3) {
println("Eat a cake")
cakesEaten++
}
// Eat a cake
// Eat a cake
// Eat a cake
var cakesEaten = 0
var cakesBaked = 0
while (cakesEaten < 3) {
println("Eat a cake")
cakesEaten++
}
do {
println("Bake a cake")
cakesBaked++
} while (cakesBaked < cakesEaten)
// Eat a cake
// Eat a cake
// Eat a cake
// Bake a cake
// Bake a cake
// Bake a cake
# 函数
fun sum(x: Int, y: Int): Int {
return x + y
}
fun main() {
println(sum(1, 2))
// 3
}
# 命名入参
类似于 python 的入参定义,可更改入参顺序
fun printMessageWithPrefix(message: String, prefix: String) {
println("[$prefix] $message")
}
fun main() {
// Uses named arguments with swapped parameter order
printMessageWithPrefix(prefix = "Log", message = "Hello")
// [Log] Hello
}
# 默认参数
fun printMessageWithPrefix(message: String, prefix: String = "Info") {
println("[$prefix] $message")
}
fun main() {
// Function called with both parameters
printMessageWithPrefix("Hello", "Log")
// [Log] Hello
// Function called only with message parameter
printMessageWithPrefix("Hello")
// [Info] Hello
printMessageWithPrefix(prefix = "Log", message = "Hello")
// [Log] Hello
}
# 无返回值
没有声明返回类型的,则返回类型(隐式)为 Unit
fun printMessage(message: String) {
println(message)
// `return Unit` or `return` is optional
}
fun main() {
printMessage("Hello")
// Hello
}
# 单行表达式函数
fun sum(x: Int, y: Int) = x + y
fun main() {
println(sum(1, 2))
// 3
}
# Lambda 表达式
// 一般写法
fun uppercaseString(text: String): String {
return text.uppercase()
}
fun main() {
println(uppercaseString("hello"))
// HELLO
}
// Lambda 写法
fun main() {
val upperCaseString = { text: String -> text.uppercase() }
println(upperCaseString("hello"))
// HELLO
}
示例 1:过滤
val numbers = listOf(1, -2, 3, -4, 5, -6)
val positives = numbers.filter ({ x -> x > 0 })
val isNegative = { x: Int -> x < 0 }
val negatives = numbers.filter(isNegative)
println(positives)
// [1, 3, 5]
println(negatives)
// [-2, -4, -6]
示例 2:映射
val numbers = listOf(1, -2, 3, -4, 5, -6)
val doubled = numbers.map { x -> x * 2 }
val isTripled = { x: Int -> x * 3 }
val tripled = numbers.map(isTripled)
println(doubled)
// [2, -4, 6, -8, 10, -12]
println(tripled)
// [3, -6, 9, -12, 15, -18]
# 对象编程
# 定义
// class header(类头)声明属性:在类名后的()中定义
class Contact(val id: Int, var email: String) // 声明类属性时,可以在末尾使用逗号
class Contact(val id: Int, var email: String = "[email protected]")
// class body(类体)声明属性:在{}体中定义
class Contact(val id: Int, var email: String) {
val category: String = ""
}
- 官方建议属性声明为只读(
val),除非需要在创建类的实例后更改它们
# 构造函数
- 默认情况下,Kotlin 会自动创建一个构造函数,其参数为 class header 中声明的
- 无需
new关键字
class Contact(val id: Int, var email: String)
fun main() {
val contact = Contact(1, "[email protected]")
}
# 访问属性
在实例名称后添加句点 . ,然后写出属性名称
class Contact(val id: Int, var email: String)
fun main() {
val contact = Contact(1, "[email protected]")
// Prints the value of the property: email
println(contact.email)
// [email protected]
// Updates the value of the property: email
contact.email = "[email protected]"
// Prints the new value of the property: email
println(contact.email)
// [email protected]
}
扩展知识:也可以字符串模板中使用
println("Their email address is: ${contact.email}")
# 成员函数
在 class body 中定义
class Contact(val id: Int, var email: String) {
fun printId() {
println(id)
}
}
fun main() {
val contact = Contact(1, "[email protected]")
// Calls member function printId()
contact.printId()
// 1
}
# 数据类
类似于 Java 中的 Bean 类、模型类、实体类
data class User(val name: String, val id: Int)
kotlin 的类,默认没有继承 Object,因此没有 toString 、 equals 等 Object 类方法
toString
// toString
val user = User("Alex", 1)
// Automatically uses toString() function so that output is easy to read
println(user)
// User(name=Alex, id=1)
equals
val user = User("Alex", 1)
val secondUser = User("Alex", 1)
val thirdUser = User("Max", 2)
// Compares user to second user
println("user == secondUser: ${user == secondUser}")
// user == secondUser: true
// Compares user to third user
println("user == thirdUser: ${user == thirdUser}")
// user == thirdUser: false
Copy
val user = User("Alex", 1)
// Creates an exact copy of user
println(user.copy())
// User(name=Alex, id=1)
// Creates a copy of user with name: "Max"
println(user.copy("Max"))
// User(name=Max, id=1)
// Creates a copy of user with id: 3
println(user.copy(id = 3))
// User(name=Alex, id=3)
# Null 值安全机制
- Kotlin 采用了空值安全机制。该机制能在编译时检测
null值可能存在的问题,而无需等到程序运行时再发现。 - 默认下所有类型都不能为
null,必须定义,如果需要为null,则在类型后添加?
fun main() {
// neverNull has String type
var neverNull: String = "This can't be null"
// Throws a compiler error
neverNull = null
// nullable has nullable String type
var nullable: String? = "You can keep a null here"
// This is OK
nullable = null
// By default, null values aren't accepted
var inferredNonNull = "The compiler assumes non-nullable"
// Throws a compiler error
inferredNonNull = null
// notNull doesn't accept null values
fun strLength(notNull: String): Int {
return notNull.length
}
println(strLength(neverNull)) // 18
println(strLength(nullable)) // Throws a compiler error
}
# 使用安全的调用方式
fun lengthString(maybeString: String?): Int? = maybeString?.length
fun main() {
val nullString: String? = null
println(lengthString(nullString))
// null
}
person.company?.address?.country
# Elvis 运算符
通过 Elvis 运算符 ?: 检测到 null 值,你可以指定一个默认值来作为返回值
fun main() {
val nullString: String? = null
println(nullString?.length ?: 0)
// 0
}
# 扩展函数
类似于 Java 的类继承,但又不太一样,因为它是直接作用在父类上的。
fun String.bold(): String = "<b>$this</b>"
fun main() {
// "hello" is the receiver
println("hello".bold())
// <b>hello</b>
}
class HttpClient {
fun request(method: String, url: String, headers: Map<String, String>): HttpResponse {
// Network code
}
}
// 扩展常用方法.get .post
fun HttpClient.get(url: String): HttpResponse = request("GET", url, emptyMap())
fun HttpClient.post(url: String): HttpResponse = request("POST", url, emptyMap())
# 作用域函数
Kotlin 总共有五个作用域相关函数: let 、 apply 、 run 、 also 和 with 。
# let (类匿名函数)
由于 kotlin 有严格的 null 安全机制,因此每次调用函数,都是要添加判空逻辑,kotlin 有 let 语法来处理这一问题。
fun sendNotification(recipientAddress: String): String {
println("Yo $recipientAddress!")
return "Notification sent!"
}
fun getNextAddress(): String {
return "[email protected]"
}
fun main() {
val address: String? = getNextAddress()
// Argument type mismatch: actual type is 'String?', but 'String' was expected.
sendNotification(address)
}
处理:
val address: String? = getNextAddress()
val confirm = address?.let {
sendNotification(it) // `it` 来引用 `address` 变量
}
# apply (初始化对象)
- 传统写法,需要将初始化逻辑和其他逻辑代码混在一块
- kotlin 的 apply 函数则将初始化的函数单独作用域,提高可读性
class Client() {
var token: String? = null
fun connect() = println("connected!")
fun authenticate() = println("authenticated!")
fun getData() : String {
println("getting data!")
return "Mock data"
}
}
val client = Client()
fun main() {
client.token = "asdf"
client.connect()
// connected!
client.authenticate()
// authenticated!
client.getData()
// getting data!
}
val client = Client().apply {
token = "asdf"
connect()
// connected!
authenticate()
// authenticated!
}
fun main() {
client.getData()
// getting data!
}
# run (初始化对象)
相当于带返回值的 apply
val client: Client = Client().apply {
token = "asdf"
}
fun main() {
val result: String = client.run {
connect()
// connected!
authenticate()
// authenticated!
getData()
// getting data!
}
}
# also
多用于调试
fun main() {
val medals: List<String> = listOf("Gold", "Silver", "Bronze")
val reversedLongUppercaseMedals: List<String> =
medals
.map { it.uppercase() }
.filter { it.length > 4 }
.reversed()
println(reversedLongUppercaseMedals)
// [BRONZE, SILVER]
}
fun main() {
val medals: List<String> = listOf("Gold", "Silver", "Bronze")
val reversedLongUppercaseMedals: List<String> =
medals
.map { it.uppercase() }
.also { println(it) }
// [GOLD, SILVER, BRONZE]
.filter { it.length > 4 }
.also { println(it) }
// [SILVER, BRONZE]
.reversed()
println(reversedLongUppercaseMedals)
// [BRONZE, SILVER]
}
# with
class Canvas {
fun rect(x: Int, y: Int, w: Int, h: Int): Unit = println("$x, $y, $w, $h")
fun circ(x: Int, y: Int, rad: Int): Unit = println("$x, $y, $rad")
fun text(x: Int, y: Int, str: String): Unit = println("$x, $y, $str")
}
fun main() {
val mainMonitorPrimaryBufferBackedCanvas = Canvas()
mainMonitorPrimaryBufferBackedCanvas.text(10, 10, "Foo")
mainMonitorPrimaryBufferBackedCanvas.rect(20, 30, 100, 50)
mainMonitorPrimaryBufferBackedCanvas.circ(40, 60, 25)
mainMonitorPrimaryBufferBackedCanvas.text(15, 45, "Hello")
mainMonitorPrimaryBufferBackedCanvas.rect(70, 80, 150, 100)
mainMonitorPrimaryBufferBackedCanvas.circ(90, 110, 40)
mainMonitorPrimaryBufferBackedCanvas.text(35, 55, "World")
mainMonitorPrimaryBufferBackedCanvas.rect(120, 140, 200, 75)
mainMonitorPrimaryBufferBackedCanvas.circ(160, 180, 55)
mainMonitorPrimaryBufferBackedCanvas.text(50, 70, "Kotlin")
}
val mainMonitorSecondaryBufferBackedCanvas = Canvas()
with(mainMonitorSecondaryBufferBackedCanvas) {
text(10, 10, "Foo")
rect(20, 30, 100, 50)
circ(40, 60, 25)
text(15, 45, "Hello")
rect(70, 80, 150, 100)
circ(90, 110, 40)
text(35, 55, "World")
rect(120, 140, 200, 75)
circ(160, 180, 55)
text(50, 70, "Kotlin")
}
上次更新: 2026/08/13, 14:48:29