Kotlin官方参考整理——01.开始
2017-06-29 21:56
351 查看
前言
这是前段时间学习Kotlin官方参考(中 / 英)时所做的笔记,相比官方参考,这些笔记:为了方便根据笔记去查阅官方参考,笔记的整体结构与官方参考基本保持一致
对官方参考中一些零散分布的知识点进行了整合
对官方参考中个别讲的比较笼统的知识点做了详细展开
将官方参考中一些比较晦涩的知识点,使用个人认为更容易理解的方式进行了阐述
笔记并未100%覆盖官方参考中的内容
这份笔记基本涵盖了Kotlin的大部分知识点,可以作为 Kotlin快速入门 或是 官方参考的导读 来使用。
另外,因为个人也正处于学习Kotlin的过程中,因此笔记中的理解和论述难免会有错误,希望读者朋友不吝指正,大家共同学习进步。
1开始
1.1 基本语法
1.1.1 定义包
package foo.bar //kotlin中语句结尾不需要写分号 var aaa = 1 fun baz() { } class Goo { } ……
包的声明应处于源文件顶部。不同于Java,Kotlin中包名可以不用与源文件所在的目录结构对应,也就是说源文件可以随便移动而不用修改其包名。
Kotlin源文件的结构与C++类似,即变量、函数、类可以同级(如上面代码所示),而Java中,变量和函数都在类中。
源文件所有内容都包含在声明的包内。所以上例中 baz() 的全名是 foo.bar.baz 、Goo 的全名是foo.bar.Goo。如果没有指明包,该文件的内容属于无名字的默认包。
1.1.2 访问权限修饰符(又称为可见性修饰符)
Kotlin中变量、函数、类有三种位置,分别是:顶层位置:即在源文件中顶层
类中
局部位置:即在代码块中或函数中
Kotlin中有四种可见性修饰符,分别为public(默认)、internal、protected、private。可以修饰顶层位置和类中的变量、函数、类,不能修饰局部位置的变量、函数、类。
顶层位置的可见性修饰符:
public:随处可见,默认。
internal:本模块内可见。
protected:不可用于顶层位置的变量、函数、类。
private:本文件内可见。
类中的可见性修饰符:
public:能见到类的任何人都可见。
internal:在本模块内,能见到类的任何人都可见。
protected:本类和子类中可见。
private:本类中可见。
关于可见性的应用,举一个简单直观的例子:
/* *文件File1.kt */ package cn.szx.kotlindemo //默认可见性为public fun function1(){ print("function1 in File1") } //--------------------------------------------------------------------------------------- /* *文件File2.kt */ package cn.szx.kotlindemo fun function2(){ //File2中可以访问File1中定义的函数function1() //并且因为File2和File1在同一个包内,因此访问function1()时不用加包名前缀 function1() } //编译报错,不能在同一个包内再定义一个function1()了 //因为此处的function1()和File1中的function1(),其全名都是cn.szx.kotlindemo.function1() //系统无法区分二者 //fun function1(){ // //} //function1(a:Int)与File1中的function1()形参列表不同,可以区分,因此可以定义 fun function1(a:Int){ print("function1 in File2"+a) } //--------------------------------------------------------------------------------------- /* *文件File3.kt */ package cn.szx.kotlindemo2 //import cn.szx.kotlindemo.function1 fun function3(){ //File3和File1不在同一个包内,因此访问function1()时需要加包名前缀 cn.szx.kotlindemo.function1() //使用“import cn.szx.kotlindemo.function1”将function1导入后,则可以省略前缀 //“import cn.szx.kotlindemo.function1”代表将cn.szx.kotlindemo包内所有名为function1的函数都导入了 //function1() } //允许定义function1()。与File1中的function1()的包名前缀不同,因此系统可以区分 fun function1(){ print("function1 in File3") }
1.1.3 定义函数
基本规则:使用fun关键字来定义函数,形参的定义格式为“形参名:形参类型”。//带有两个 Int 参数、返回 Int 的函数 fun sum(a: Int, b: Int): Int { return a + b } //将表达式作为函数体、返回值类型⾃动推断的函数 fun sum(a: Int, b: Int) = a + b //没有返回值的函数 fun printSum(a: Int, b: Int) { println("sum of $a and $b is ${a + b}") }
1.1.4 定义变量
注意:abstract只能修饰成员位置(即类中)的变量和函数,不能修饰顶层位置和局部位置的变量和函数。定义变量的基本格式:
var a:Int = 1//可变变量 val b:Int = 2//不可变变量 var c = 3//根据等号右边的值自动推断c的类型 var d:Int//定义时不进行初始化,则Int不能省略
var定义的是可变变量,即一般的变量,val定义的是不可变变量,即符号常量,类似于Java中的final变量或者C++中的const变量。
此外,处于不同位置的变量,其初始化要求是不同的:
顶层变量必须进行初始化。(除非其没有幕后字段,详见《03类和对象1.md》->“属性”)
类中的变量要么进行初始化(可以直接后接等号进行初始化,也可以在构造函数或初始化块(init块)中初始化),要么声明为abstract。(除非其没有幕后字段,详见《03类和对象1.md》->“属性”)
局部变量可以不初始化。
package cn.szx.kotlindemo var a: Int = 1 //顶层变量,必须进行初始化 private class Test{ //abstract var a:Int //类中的变量,声明为抽象 var b: Int = 2 //类中的变量,初始化 } private fun baz() { var a: Int //局部变量,可以不初始化 }
1.1.5 字符串
字符串用String类型表示。字符串是不可变的。字符串的元素——字符可以使用索引运算符访问: s[i] 。可以用for循环迭代字符串:for (c in str) { println(c) }
Kotlin有两种类型的字符串字面值: 转义字符串和原生字符串。
转义字符串支持转义字符,很像Java中的字符串:
val s = "Hello, world!\n"
原生字符串使用三个引号( “”” )分界符括起来,不支持转义,任何字符都会被原样展示(包括换行、空格、tab等):
val text = """ for (c in "foo") print(c) """
字符串模板
字符串可以包含模板表达式,使用时会用模板表达式的值来替换掉模板表达式。模板表达式以美元符( $ )开头,由一个简单的名字构成,如:
val i = 10 val s = "i = $i" // s为"i = 10"
或者用花括号扩起来的任意表达式:
val s = "abc" val str = "$s.length is ${s.length}" // str为"abc.length is 3"
转义字符串和原生字符串都支持模板表达式,那如果我们想要的就是$怎么办呢——使用
${'$'}
1.1.6 条件表达式
Java中的很多语句,在kotlin中都既是语句,也是表达式(即本身可以代表一个值,可以放在等号右边)。例如if表达式://if的分支可以是代码块,最后的表达式的值作为该块的值 val max = if (a > b) { print("Choose a") a } else { print("Choose b") b } //也可以像下面这样,类似于Java中的?:表达式 val max = if (a > b) a else b
1.1.7 空安全
1.1.7.1 非空类型与可空类型
默认情况下,所有类型都是非空的,即我们告诉编译系统,该类型变量的值不可能是null,因此我们也不能将null赋值给该类型的变量:var a: String = null //非空类型取值不能为null,编译报错 var b: String = "hello" val l = b.length //因为b是非空类型的,所以b.length必然是安全的,不可能报出NPE(Null Pointer Exception)
在类型关键字后加上
?,那么就得到了一个可空类型,如
String?:
var b: String? = null //可空类型,取值可以为空 val l = b.length //编译报错,因为b是可空类型,这样调用是不安全的
那么我们想要获得可空的b的length属性该怎么办呢?有两种方法:
(1) 先进行空检测
val l = if (b != null) b.length else -1 //也支持更复杂(更智能的检测) if (b != null && b.length > 0) { print("String of length ${b.length}") } else { print("Empty string") }
(2) 使用安全调用操作符“?.”
val l = b?.length
如果b非空,就返回b.length,否则返回null,这个表达式的类型是Int?。
安全调用操作符在链式调用中很有用,例如:
bob?.department?.head?.name
只要任何一个环节为null,那么该链式调用就会返回null。
如果要只对非空值执行某个操作,可以将安全调用操作符与let一起使用:
val listWithNulls: List<String?> = listOf("A", null) for (item in listWithNulls) { item?.let { println(it) } //输出A并忽略null }
1.1.7.2 Elvis操作符
表达式1 ?: 表达式2
含义:如果表达式1不是null,那么就返回表达式1的值,否则就返回表达式2的值。注意,当且仅当表达式1的值为null时,才会对表达式2进行求值。
throw和return在Kotlin中也都是表达式,所以它们也可以用在elvis操作符右侧。这可能会非常方便:
fun foo(node: Node): String? { val parent = node.getParent() ?: return null val name = node.getName() ?: throw IllegalArgumentException("name expected") …… }
1.1.7.3 !!操作符
这是为NPE爱好者准备的:var a: String? = null //a可空 val l = a!!.length //如果引用a为null,则抛出NPE,如果不为null,则返回a.length
1.1.7.4 过滤集合中的非空元素
如果你有一个可空类型元素的集合,并且想要过滤得到非空元素,你可以使用filterNotNull来实现:val nullableList: List<Int?> = listOf(1, 2, null, 4) val intList: List<Int> = nullableList.filterNotNull()
1.1.8 类型检测与转换
1.1.8.1 类型检测与智能类型转换
类型检测使用is或!is操作符。在多数情况下,不需要在Kotlin中使用显式转换操作符,因为编译器会跟踪不可变值的is检查,并在需要时自动进行类型转换:if (obj is String) { print(obj.length) //obj被自动转换为String } if (obj !is String) { print("Not a String") }else { print(obj.length) //obj被自动转换为String } if (x !is String) return print(x.length) //x被转换为字符串,可见编译器是十分智能的 //||右侧的x被自动转换为字符串 if (x !is String || x.length == 0) return //&&右侧的x被自动转换为字符串 if (x is String && x.length > 0) { print(x.length) }
但是需要注意,当编译器不能保证变量在检查和使用之间不可改变时,智能转换不能用(详见官方参考)。
1.1.8.2 类型转换操作符as
val b:String = a as String
当无法转换时会抛出类型转换异常。
1.1.8.3 安全的类型转换操作符as?
val b:String? = a as? String
当无法转换时不会抛出类型转换异常,而是返回null。
1.1.9 For循环
Java中有普通的for循环和foreach循环(也称为高级for循环),但kotlin中不支持普通的for循环,即你不能像这样使用for循环:for(var i=0;i<100;i++){}。
kotlin中的for循环都是高级for循环。for循环可以对任何提供迭代器(iterator)的对象进行遍历,语法如下:
for(item in collection){ print(item) }
例如:
val strs = listOf("1", "2", "3")//List集合 val ints = arrayOf(1, 2, 3)//数组 for (item in strs) { print(item) } for (item in ints) { print(item) } /* * 也可以通过索引来访问 */ for (index in strs.indices) { print(strs[index]) } for (index in ints.indices) { print(ints[index]) }
1.1.10 while循环
while循环和do…while循环与Java中的用法一样val items = listOf("apple", "banana", "kiwi") var index = 0 while (index < items.size) { println("item at $index is ${items[index]}") index++ }
1.1.11 when
when即可以作为语句,也可以作为表达式,它的作用类似于C++和Java中的switch语句,不过使用起来更加灵活。when (x) { 1 -> print("x == 1") 2 -> print("x == 2") else -> { print("x is neither 1 nor 2") } } //0,1两个分支执行一样的操作 when (x) { 0, 1 -> print("x == 0 or x == 1") else -> print("otherwise") } //我们可以用任意表达式(而不只是常量)作为分支条件 when (x) { parseInt(s) -> print("s encodes x")//当x等于parseInt(s)时,执行此分支 else -> print("s does not encode x") } //我们也可以检测一个值在(in)或者不在(!in)某个区间或者集合中 when (x) { in 1..10 -> print("x is in the range") in validNumbers -> print("x is valid") !in 10..20 -> print("x is outside the range") else -> print("none of the above") } //另一种可能性是检测某个值是(is)或者不是(!is)某个特定类型的值 val hasPrefix = when(x) { is String -> x.startsWith("prefix")//会将x智能转换为String类型 else -> false } //when也可以用来取代if-else if链。如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支 when { x.isOdd() -> print("x is odd") x.isEven() -> print("x is even") else -> print("x is funny") }
1.1.12 使用区间(range)
使用..操作符来定义一个区间,如1..10。任何可比较的类型都可以定义为一个区间。区间常与in和!in配合使用。
if (i in 1..10) { //检测i是否在区间[1,10]之内 println(i) }
整型区间(IntRange、LongRange、CharRange )有一个额外的特性:它们可以迭代。
前面讲for循环时提到过,kotlin中的for循环类似于Java中的高级for循环,那么如果我们非要使用Java中普通for循环那样的for循环呢,答案就是使用区间:当我们使用for循环来迭代区间时,编译器会将其转换为类似于Java中的普通for循环。
for (i in 1..4) print(i) //输出“1234” 等价于Java中的: for(int i=1;i<=4;i++){...}
使用downTo操作符来定义一个逆向的区间:
for (i in 4 downTo 1) print(i),输出“4321”
指定迭代的步长:
for (i in 1..4 step 2) print(i) // 输出“13” for (i in 4 downTo 1 step 2) print(i) // 输出“42”
使用util操作符定义一个不含尾元素的区间:
for (i in 1 until 10) { // i in [1, 10),排除了10 println(i) }
1.1.13 使用集合
对集合进行迭代:for (item in items) { println(item) }
使用in运算符来判断集合内是否包含某实例:
when { "orange" in items -> println("juicy") "apple" in items -> println("apple is fine too") }
使用lambda表达式来过滤(filter)和映射(map)集合:
fruits .filter { it.startsWith("a") } .sortedBy { it } .map { it.toUpperCase() } .forEach { println(it) }
1.2 习惯用法
详见官方参考。建议暂时搁置,最后再看。1.3 编码规范
详见官方参考。建议暂时搁置,最后再看。相关文章推荐
- Kotlin官方参考整理——04.函数和lambda表达式
- Kotlin官方参考整理——05.其他
- Kotlin官方参考整理——06.Java互操作
- Kotlin官方参考整理——02.基础
- Kotlin官方参考整理——03.类和对象1
- Kotlin官方参考整理——03.类和对象2
- 《swift2.0 官方教程中文版》 第3章-01关于语言参考&词法结构
- Spring Data JPA 指南——整理自官方参考文档
- Kotlin 官方参考文档 中文版_kotlin-reference-chinese.pdf
- Google官方指导文章:开始用Kotlin开发Android
- 【caffe】Caffe的Python接口-官方教程-01-learning-Lenet-详细说明(含代码)
- Yii框架官方教程增补篇2——开始:安装Yii框架
- Spring4.x官方参考文档中文版——第21章 Web MVC框架(31)
- 学习opengl官方指南 01 opengl介绍
- 数据结构之插入排序(直插,折半插,2路插)—参考整理严蔚敏数据结构
- 学习整理01-java将汉字转换为拼音
- Gentoo Linux安装详解--根据官方WiKi整理
- 自学Swift-斯坦福笔记整理(第四天开始)
- python Django 1.7 中文入门 (官网) 01 开始
- 01_从此哥看破红尘,要开始习惯写技术文档了。