回到目录
项目源码 hello-kotlin 项目
上一节我们用 gradle + spring boot 搭建好了学习 kotlin 的开发环境, 这一节我们解读
一下这些代码是怎么回事(gradle 和 spring boot 的知识不在本文讨论范围).
Kotlin 源文件
Kotlin 源码文件都以 kt 作为后缀, 文件名是可以任意的. 最终编译后, 会生成"文件名Kt"
的 class 文件. 例如 HelloKotlinApplication.kt 文件会被编译成 HelloKotlinApplicationKt.class
Kotlin 的 main() 方法
项目中的 HelloKotlinApplication.kt 文件里存放了 main() 方法:
fun main(args: Array) {
// 忽略下面这句, 这个是启动 springboot 的
runApplication(*args)
// 这里才是我们写的打印语句
println("Hello, Kotlin!")
}
看源代码, 我们惊奇的发型 main() 方法并没有放在类里面, 这个在 kotlin 中称为"包级方法"或"顶级方法".
实际上, 底层实现时, mian() 是放在 HelloKotlinApplicationKt 类里的一个静态方法.
就跟 java 中的 main() 入口方法一样.
// 反编译字节码为 java 代码后的样子
public final class HelloKotlinApplicationKt {
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
String var1 = "Hello, Kotlin!";
System.out.println(var1);
}
}
方法
Kotlin 中定义一个方法是用 fun 关键字的, 完整语法是 可见修饰符 fun 函数名(参数列表): 返回值 { 函数体 }
如果没有返回值, 可以省略不写;
参数的语法是 变量: 类型
省略可见修饰符的话, 默认是 public 的.
println()
我们继续看 println("Hello, Kotlin!")
这一句, 它没有分号! 没有看到调用者!
Kotlin 中不用写分号, 这个太棒了, 记住这点就可以(一开始习惯改不了没关系, IDE 会
提醒你的.)
我们重点需要关注的点是, println() 这个方法怎么就凭空冒出来了呢? 我们 ctrl + 左键
追踪一下源码看看:
@file:JvmName("ConsoleKt")
package kotlin.io
// ...
@kotlin.internal.InlineOnly
public actual inline fun println(message: Any?) {
System.out.println(message)
}
// ...
首先 @file:JvmName("ConsoleKt")
是告诉编译器, 将这个文件编译为 ConsoleKt.class.
这是一个 inline 方法, println(message: Any?) 方法将不会生成函数对象, 而是编译后
直接将 System.out.println(message)
这句代码替换到调用 println() 函数的地方.
我们再看下 HelloKotlinApplication.kt 反编译后的 java 代码:
// 内联函数调用的地方, 编译器为我们生成的代码
String var3 = "Hello, Kotlin!";
System.out.println(var3);
自动导包
上面解释了没有调用对象的问题, 但还有一个问题没有解释清楚, 那就是没有 import 语句
怎么就能直接用 println() 函数了呢. 我们第一反应肯定是编译器帮我们默认导包了, 就像
groovy 中的行为一样.
没错, 去官方文档上查找一番, 我们发现 kotlin 已经默认帮我们导入了很多包了:
- kotlin.*
- kotlin.annotation.*
- kotlin.collections.*
- kotlin.comparisons.* (since 1.1)
- kotlin.io.*
- kotlin.ranges.*
- kotlin.sequences.*
- kotlin.text.*
可以看到, println() 方法所在的 io 包已经被导入了.
包级方法并不需要用 类名.方法名的方式调用
Kotlin 中, 一个包级方法只要导入包后, 不需要再像 java 调用静态方法那样前面加个类名.
在底层, 编译器会帮我们做这项工作.
做个实验, 在 test 下,
新建 cn.codergege.demo.io 下 MyConsole.kt 文件, 定义了一个包级方法 myprintln():
@file:JvmName("MyConsoleKt")
package cn.codergege.demo.io
fun myprintln(message: Any?) {
System.out.println(message)
}
然后新建 testio 下 MyConsoleTest 文件, 使用 myprintln() 方法:
// 导入我们 io 包下的所有类
import cn.codergege.demo.io.*
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest
class MyConsoleTest {
@Test
fun testMyprintln() {
// 导入包后, 包级方法就可以直接用了.
myprintln("aa")
}
}
总结
这一节我们研究了 kotlin 源文件编译成 class 文件后文件名会改变; main() 是一种包级
方法; println() 方法是如何被调用的;
回到目录