本篇内容清单如下:
- 包
- 函数
- 变量
- 基本类型
- 控制流
一、基本语法
1. 包定义与导入
- 同 Java,在文件顶部声明:
package(修饰符) 包名
。 - 源文件的 所有内容(类、函数)都包含在 声明的 包内。
- 若没有指明包名,则文件内容属于 无名字的 默认包。
导入:
- 导入包:
import packageName
。 - 若出现类名冲突,则可使用
as
关键字本地重命名消除歧义。【1】
import org.example.Message
import org.test.Message as TestMessage
import
关键字使用场景:
- 导入
「类」
- 导入
「顶层函数 及 属性」
; - 导入 在
「对象声明」
中声明的函数和属性; - 导入
「枚举常量」
。
2. 程序入口
kotlin 应用程序的入口点是 main
函数。(虽如此,但是以下 main 无法再 AS 中识别。)
fun main() { println("hello word !") }
// 可识别运行的 main 方法
companion object {
@JvmStatic
fun main(args: Array<String>) {
println("hello word !")
}
}
3. 函数
- 声明:使用
fun
关键字。 - 形式:
fun 函数名(参数名: 类型, 参数名: 类型): 返回值类型 { // 函数体 }
。每个参数,必须有显式类型。 - 调用:
sum(1 + 2)
一个加法函数的两种写法:
// 1. 完整写法
fun sum(a: Int, b: Int): Int {
return a + b
}
// 2. 函数体仅含 单个表达式,并且 返回值 可 自动推断类型
fun sum(a: Int, b: Int) = a + b
// 3. 返回值无意义(类比 void),则 Unit 返回类型可省略
fun printSum(a: Int, b: Int): Unit { // => fun printSum(a: Int, b: Int) { ... }
println("sum of $a and $b is ${a + b}")
}
以上为一般函数调用,当然还有其他函数类型写法与调用,存在一些不同。同函数相关的内容主要有以下这五点:
- 函数声明
- 函数参数
- 函数返回值
- 函数调用
- 函数作用域
与 Java 很大不同的内容点:
- 默认参数 && 具名参数 && 可变参数
- 中缀表示法
- 局部函数(闭包)
- 扩展函数
- 高阶函数 && Lambda 表达式
根据文档整理出来的导图(函数作用域后续文章还会继续说道)如下:
[说明:星星 - 新概念且重要,问号 - 暂不理解,红旗 - 新概念但不重要。]
备注:
- 主构造函数很特别,参数可以使用 val 或 var 声明,但是普通函数不能用。次构造函数没啥大差别。
class Person constructor() { }
class Person constructor(name: String)
class Person(name: String)
class Person public constructor(name: String)
class Person(name: String) {
var children: MutableList<Person> = mutableListOf()
// 次构造函数,必须要委托给 主构造函数
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
4. 变量
两种类型:
- 只读变量,使用关键字
val
定义。只能为其赋值一次。 - 可重新赋值的变量,使用关键字
var
定义。
变量又可分为:
- 顶层变量:在文件最顶部声明的变量
- 属性与字段:类中或函数中声明与定义。
val a: Int = 1 // 立即赋值
val b = 2 // 自动推动出类型,可省略 参数类型
// b = 3 // 重新赋值,编译器会报错
var x = 5 // 自动推断类型
x += 5
二、基本类型
在 Kotlin 中,也是 “一切皆为对象”。但有些类型 可以在运行时,表示为「原生类型值」,如 数字、字符以及布尔值。
Kotlin 中的基本类型:
- 数字:Byte、Short、Int、Long、Float、Double
- 字符:Char
- 布尔值:Boolean / true false
- 数组:Array
- 字符串:String
1. 数字
- Long、Float、Double 类型同 Java 一致,后面可带 L、f/F、D 。
- Kotlin 中「无隐式拓宽转换」,如具有
Double
类型参数的函数,只能接收 Double 类型的值,而不能对Float、Int
或其他数字值调用。 - 若需要原生类型 对应的
可空引用
或泛型
,如Int
可空引用对应为Int?
,后者会把数字进行装箱
。 [注:数字装箱不一定保留同一性。===
判断同一性。 ]
每个数字类型 都支持如下 转换函数:
- toByte(): Byte
- toShort(): Short
- toInt(): Int
- toLong(): Long
- toFloat(): Float
- toDouble(): Double
- toChar(): Char
2. 字符
- 不能直接当做数字。
- 字面值使用单引号 '1' 。
3. 布尔
- 同 java ,值 true 与 false 。
- 布尔运算:|| 、 &&、 !
4. 数组
- 用 Array 表示,定义可 get 与 set 函数,可以转变为
[]
。 - 构建数组:arrayOf(1, 2, 3)
- 原生类型数组:ByteArray、IntArray 等。
5. 字符串
- 用 String 表示。字符串是不可变的,元素访问,可用索引运算符访问:
s[i]
。 - 访问字符串:
for (c in str) { } 、str[0]
- 拼接字符串,可以使用操作符
+
,但不建议,一般都使用字符串模板 $
或原始字符串
。
原始字符串
-
原始字符串
:使用三个引号 (""") 分解符,内部 无转义 且 包含换行以及任何其他字符。""" println("hello world!")""" 。另可默认|
作为边界前缀。
var text = """
for (c in "foo")
print(c)
"""
// 去除前导空格
var text = """
|Tell me and I forget.
| Teach me and I remember.
""".trimMargin()
字符串模板
- 字符串字面值 可使用 模板表达式,以 美元符 $ 开头。
- 单个变量,可省略花括号,如 {a + b} 。
整理导入如下:
三、控制流
同 Java 有些差别,Kotlin 控制类分为:
- if
- when
- for
- while
以下仅整理不一致的点。
if 表达式
- 若为单个 if 表达式,则可以直接 返回值或 赋值给变量,但必须要有 else 分支。
val max = if (a > b) a else b
when 表达式
- 取代了 C 语言的
switch
语句。 - 分支条件,可以是单个变量,也可以是 某个表达式,只要满足条件,则执行该分支的内容。
- 若 多个分支 处理方式一致,则可以将分支条件放在一起,用
,
隔开。
val x = 3
when (x) {
1 -> print("x == 1")
2,3 -> print("x > 1")
"1".toInt() -> print("it's 1")
in 1..10 -> print("x is in the range")
!in 10..20 -> print("x is outside the range")
is Int -> print("x is Int type")
else -> print("none of above")
}
Break 与 Continue 标签
- Kotlin 中任何表达式都可以使用 标签 来标记。标签的格式:
@标识符
。 - 函数嵌套,可返回到指定标签处。
四、习惯用法
五、编码规范
六、DEMO 练习
首先写一个 Java Demo,然后对比一下 Kotlin 的写法。具体内容为:一个 User Model 类,一个 UserManager 单例类,再加上 main() 入口函数调用(均是内部静态类写法)。
这里面涉及到关于 kotlin 的内容点:
- 变量声明
- 函数声明
- 空值与 null 检测
- 类与对象
- 单例类
- 静态方法
这是我之前学 kotlin 时,特别想知道如果 kt 来写,会是怎样的。当然对于 static 修饰的变量和方法,在 kt 中都是包裹在 compion object 中,称为「伴生对象」,这一点首次接触,感觉非常新奇。
以下为 Java 实现:
public class JavaDemo {
public static void main(String[] args) {
User user = new User();
user.name = "cc";
user.age = 24;
user.address = "Shanghai";
UserManager.getInstance().addUser(user);
UserManager.getInstance().addUser(new User("kk", 25));
}
public static class UserManager {
private static UserManager sInstance;
private List<User> users = new ArrayList<>();
public static UserManager getInstance() {
if (sInstance == null) {
synchronized(UserManager.class) {
if (sInstance == null) {
sInstance = new UserManager();
}
}
}
return sInstance;
}
private UserManager() {}
public void addUser(User user) {
if (!users.contains(user)) {
users.add(user);
}
}
public void addUsers(List<User> users) {
if (!this.users.containsAll(users)) {
this.users.addAll(users);
}
}
public void removeUser(User user) {
users.remove(user);
}
public void removeByIndex(int index) {
users.remove(index);
}
public void clear() {
users.clear();
}
}
public static class User {
private String name;
private int age;
private String address;
public User() {}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
}
Kotlin 写法如下:
import java.util.*
class JavaVSKotlinDemo {
companion object {
@JvmStatic
fun main(args: Array<String>) {
val user = User()
user.name = "cc"
user.age = 24
user.address = "Shanghai"
UserManager.getInstance().addUser(user)
UserManager.getInstance().addUser(User("kk", 25))
}
}
class UserManager private constructor() {
private val users = ArrayList<User>()
fun addUser(user: User) {
if (!users.contains(user)) {
users.add(user)
}
}
fun addUsers(users: List<User>) {
if (!this.users.containsAll(users)) {
this.users.addAll(users)
}
}
fun removeUser(user: User) {
users.remove(user)
}
fun removeByIndex(index: Int) {
users.removeAt(index)
}
fun clear() {
users.clear()
}
companion object {
// 单例模式
private var sInstance: UserManager? = null
get() {
return field ?: UserManager()
}
@JvmStatic
@Synchronized
fun getInstance(): UserManager {
// synchronized(UserManager::class.java)
return requireNotNull(sInstance)
}
}
}
class User constructor() {
var name: String? = null
var age: Int = 0
var address: String? = null
constructor(name: String?, age: Int) : this() {
this.name = name
this.age = age
}
}
}