解读 hello-kotlin 项目

回到目录
项目源码 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() 方法是如何被调用的;


回到目录

你可能感兴趣的:(解读 hello-kotlin 项目)