Kotlin系列之Kotlin基础
本篇内容包括
- Kotlin 中声明函数,变量,类,枚举,属性
- Kotlin 中的控制结构
- Kotlin 的智能类型转换
- Kotlin 对异常的处理
<p>看完这篇文章后读者应该能够看懂用 Kotlin 编写的代码,同时学到一些 Kotlin 具有的特性,比如智能类型转换。还会看到 Kotlin 和 Java 一些不同的地方。 当然这些不同之处都是从实用,简洁的角度出发。不过具体感觉如何还要从每个人自己的感受来看。</p>
基本要素函数 & 变量
先来看看 Kotlin 中主函数的声明热热身
class CornerstoneApplicationfun main(args: Array<String>) {
println("hello , kotlin!")
}
- 声明函数的关键字:fun
- 函数可以定义在文件的最外层,不一定需要把它放入到类中
- 数组用 Array 类表示这一点和 Java 不同,Kotlin 中没有声明数组类型的特殊语法
- 使用 println 完成和 Java 中 System.out.println 同样的功能 , 但是更加简短
- 每一行代码的结尾可以省略分号 ";"
函数
现在我们来声明一个完整的函数,有参数,有返回值.
fun max1(a: Int , b: Int): Int { if (a > b) {
return a
} else {
return b
}
}
从这个函数声明我们可以发现 Kotlin 的一些特点:
- 函数名称紧跟在 fun 关键字之后
- 参数名在前,参数类型在后 ,中间用 ":" 分隔
- 函数的返回值类型写在参数列表最右侧的括号外中间用 ":" 分隔
<p>在看完这个函数声明后可能你并不会觉得它有什么特别的,除了函数声明部分与 Java 有不同之处,但是大体上看来也没多大区别。你可能会问 Kotlin 承诺的简洁性呢? 别着急如果 Kotlin 没有兑现它承诺的简洁性,我也不会写这篇文章。 下面就用 Kotlin 的特性来改写这个函数:</p>
fun max2(a: Int , b: Int): Int { return if(a > b) a else b
}
<p>比之前的函数更加简洁了一些,我省略了两对括号 "{}" 和一个 return 语句。 从上面这个函数你大概可以猜测出 return 语句返回的是 ```if(a > b) else b``` 的结果。 你猜测的没错,答案确实如此,下面就来解释一下。</p>
语句(statement) 和表达式(expression)
- 表达式是有值的,并且可以作为另一个表达式的一部分使用
- 语句总是包围着它的代码块中的顶层元素,并且不会产生值
<p>在 Kotlin 中 if 是表达式,而在 Java 中 if 是语句。在 Kotlin 中还有很多控制结构都是表达式我们会在之后遇到在做说明。</p>
Kotlin 的函数还能够更加简洁比起 max2 版本的函数,说到这里的时候我仿佛可以听到 Kotlin 的开发者在对我说 you have my word!
表达式函数体
由于我们的 max 函数体是由单个表达式构成的,所以我们还可以让函数变得更加简洁。
fun max3(a: Int , b: Int) = if (a > b) a else b
<p>我们又省略了一对花括号 "{}" 和 一个 return 语句还有声明返回值的 ": Int" 语句。在这里不用担心省略了返回值类型会引起错误,因为编译器会分析作为函数体的表达式,并且把表达式的类型作为函数的返回值类型,所以并不需要显示的声明出来,这种通过上下文分析出类型的一般称为智能类型推导。</p>
变量
声明变量的关键字
- val (value) : 不可变引用。使用 val 声明的变量不能在初始化之后再次赋值。它对应 Java 中的 final 变量。
- var (variable) : 可变引用。这种变量的值是可以改变的。对应的是 Java 中的普通非 final 变量。
<p>在 Kotlin 中声明变量时可以不指定变量的数据类型,因为编译器可以更具上下文进行类型推导,例如:</p>
val a = 66val b = 7.5e6
<p>编译器会根据 66 和 7.5e6 推断出 变量 a 的类型为 Int , 变量 b 的类型为 Double 。当然前提是必须有 66 和 7
.5e6 才能进行推导 , 如果不能提供可以付给这个变量的值的信息编译器就没法进行类型推导。</p>
字符串模板
<p>字符串模板是 Kotlin 的新特性</p>
fun hello(name: String) { println("hello $name")
}
<p>我们在字符串中使用了变量 name ,通过 $name 的方式,这等价于 Java 中用 + 号链接字符串。当你试图在字符串中使用 $ 字符时,需要对它进行转义, println("$") 。还可以引用更复杂的表达式或者函数比如 ${name.length}</p>
类和属性
<p>在 Kotlin 中定义一个类使用 class 关键字这一点和 Java 相同。先来看一个 Java 类 </p>
public class Person { private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
<p>啊...写完这个类之后我深吸了一口气,怎么说呢就是有一种很烦躁的感觉,可以看到 Java 类中充满了很多模板代码 , 比如构造函数中的参数赋值,每个成员变量的 getter 和 setter 函数。而在 Kotlin 中这些都是可以省略掉显示编码的。</p>
class Person(var name: String)
<p>好了我用 Kotlin 定义了一个 Person 类 ,是不是很舒服</p>
属性
<p>在 Java 中,字段和其访问器(getter & setter 函数)的组合常常被叫做属性,而许多的框架严重依赖这个概念。在 Kotlin 中,属性是头等的语言特性,完全代替了字段和访问器方法。 </p>
class Person { val name: String
var isMarried: Boolean
}
- val 只读属性:生成一个字段和一个简单的 getter
- var 可写可读:生成一个字段,一个getter和一个setter
<p>当声明属性的时候就声明了对应的访问器。访问器的默认实现是非常简单的,类似这样:</p>
fun getName(): String { return name
}
fun setName(name: String) {
this.name = name
}
<p>如果默认的简单访问器不能满足需求还可以自定义访问器: 这里我们给 isSquare 属性自定义了 getter 访问器。</p>
class Rectangle(val height: Int , val width: Int) { val isSquare: Boolean
get() = height == width
}
Kotlin 源码布局:目录和包
<p>在 Java 中所有的类组织成包。Kotlin 中也有和 Java 相似的包的概念。每一个 Kotlin 文件都可以以一条 package 语句开头,文件中定义的所有声明都会被放到这个包中。如果其他的文件中定义的声明也有相同的包,这个文件可以直接使用它们;如果包不同则需要导入后再使用。和 Java 一样导入语句通过 import 关键字。Kotlin 不区分导入的是类还是函数,而且它允许使用 import 关键字导入任何种类的声明。</p>
<p>在 Java 中包名和文件夹目录结构有着严格的规定, 比如 org.demo.Demo 类必须在 org/demo/ 文件夹下。在 Kotlin 中没有这个限制,在 Kotlin 中包命可以和文件夹结构不一致。 </p>
枚举和 when
在 Kotlin 中声明一个枚举:
enum class Color(val r: Int , val g: Int, val b: Int) { RED(255,0,0) , ORANGE(255,165,0) , YELLOW(255,255,0) ,
GREEN(0,255,0) , BLUE(0,0,255) , INDIGO(75,0,130) ,
VIOLET(238,130,238);
fun rgb() = (r * 256 + g) * 256 + b
}
<p>在 Kotlin 中 enum 是一个软关键字,:只有在当他出现在 class 关键字之前的时候才有特殊意义,在其他地方可以把他当作普通的名称使用。</p>
<p>在上面的例子中展示了 Kotlin 中唯一需要使用分号 ";" 的地方,就是在枚举中定义了任何方法的时候,需要使用分号把枚举常量列表和函数定义分开。</p>
使用 when 处理枚举值
when 可以被认为是 Java 中 switch 结构的替代品,但是它比 switch 要更加强大。在 Kotlin 中 when 是一个表达式,跟之前提到过的 if 一样同为表达式。
fun getChineseDesc(color: Color) = when (color) { RED -> "红色"
ORANGE -> "橙色"
YELLOW -> "黄色"
GREEN -> "绿色"
BLUE -> "蓝色"
INDIGO -> "靛青"
VIOLET -> "紫色"
}
<p>这是一个表达式函数体 , 这个例子很好的体现了 when 作为表达式的功能。</p>
<p>Kotlin when 表达式的特点 :</p>
- 不需要在 when 的分支上写 break 语句,如果匹配成功那么之后对应的分支会去执行
- 可以将多个值合并到同一个分支,只需要将值用逗号隔开
fun getWarmth(color: Color) = when (color) { RED , ORANGE , YELLOW -> "warm"
GREEN -> "neutral"
BLUE ,INDIGO , VIOLET -> "cold"
}
- when结构中可以使用任意对象
<p>Kotlin 中的 when 结构比 Java 中的 switch 强大得多。switch 要求必须使用常量(枚举,字符串,或者数字字面值)作为分支条件。而 when 允许使用任何对象。这个例子中 setOf 函数返回一个 Set 集合实例。 </p>
fun mix(c1: Color , c2: Color) = when (setOf(c1 , c2)) { setOf(RED , YELLOW) -> ORANGE
setOf(YELLOW , BLUE) -> GREEN
setOf(BLUE , VIOLET) -> INDIGO
else -> throw Exception("dirty color")
}
- when可以不带参数,如果没有给 when 提供参数,分支条件就可以是任意的布尔表达式
fun mixOptimized(c1: Color , c2: Color) = when { (c1 == RED && c2 == YELLOW) || (c1 == YELLOW && c2 == RED) -> ORANGE
(c1 == YELLOW && c2 == BLUE) || (c1 == BLUE && c2 == YELLOW) -> GREEN
(c1 == BLUE && c2 == VIOLET) || (c1 == VIOLET && c2 == BLUE) -> INDIGO
else -> throw Exception("dirty color")
}
智能类型转换:合并类型检查和转换
interface Exprclass Num(val value: Int) : Expr
class Sum(val left: Expr , val right: Expr) : Expr
fun eval(e: Expr): Int { if (e is Num) {
val n = e as Num
return n.value
}
if (e is Sum) {
return eval(e.left) + eval(e.left)
}
throw IllegalArgumentException("Unknown expression")
}
<p>其实在这个函数中 e as Num 是完全没必要写的 , 因为通过上下问已经可以推断出变量 e 是 Num 类型了,所以在 if 块内 用变量 e 可以直接使用 Num 的 API 了。Kotlin 中 is 和 Java 中的 instanceOf 相似,但是 Java 中即使检查通过也要强制进行一次类型转换。 </p>
fun eval(e: Expr): Int { if (e is Num) {
return e.value
}
if (e is Sum) {
return eval(e.left) + eval(e.left)
}
throw IllegalArgumentException("Unknown expression")
}
fun eval2(e: Expr): Int = when (e) { is Num -> e.value
is Sum -> eval2(e.left) + eval2(e.left)
else -> throw IllegalArgumentException("Unknown expression")
}
<p>在 Kotlin 中应该遵守的规则是:代码块中最后的表达式就是结果 , 所以可以省略 return 语句</p>
迭代事物: while 循环和 for 循环
<p>Kotlin 中的 while 循环和 do while 循环和 Java 中一模一样所以没什么可讲的 ,我们就直接跳过好了。</p>
for 循环迭代数字:区间和数列
<p>Kotlin 中没有常规的 Java for 循环。取而代之的是区间的概念。 下面代码表示一个 1 到 10 的区间, Kotlin 中的区间是包含的或者是闭合的, 意味着第二个值始终是区间的一部分。你能用整数区间做的最基本的事情就是循环迭代其中所有的值。如果你能迭代区间中所有的值,这样的区间被称为数列。</p>
val oneToTen = 1..10
for (i in 1..100) { println(i)
}
for (i in 100 downTo 1 step 2) { println(i)
}
<p>下面这个循环是从 0 开始不包含 100</p>
for (i in 0 until 100) { println(i)
}
迭代集合
迭代 list
val list = listOf("axc", "da", "dada", "3rff")for (e in list) {
println(e)
}
带有角标的迭代 , index 表示元素的角标 , e 表示元素。 这种迭代方式对于数组类型同样适用。
val list = listOf("axc", "da", "dada", "3rff")for ((index , e) in list.withIndex()) {
println("index = $index , element = $e")
}
迭代 map
val map = mapOf(1 to "One", 2 to "Two", 3 to "three")for ((key , value) in map) {
println("key = $key , value = $value")
}
Kotlin 中的异常
<p>Kotlin 中的异常处理语句基本形式和 Java 类似,抛出异常的方式也不列外。和 Java 不同的是 Kotlin 中的 throw 结构是一个表达式,能作为另一个表达式的一部分使用。 和其他许多现代 JVM 语言一样 , Kotlin 并不区分受检异常和未受检异常 , 所有不需要用 throws 关键字在函数上声明会抛出什么异常。 不用指定函数抛出的异常, 而且可以处理也可以不处理异常。这种设计是基于 Java 中使用异常的实践做出的决定。经验显示了这些 Java 规则常常导致许多毫无意义的重新抛出或者忽略异常的代码,而这些规则不能总是保护你免受可能发生的错误。</p>
<p>在 Kotlin 中 try 也是作为表达式的 , 如果一个 try 代码块执行一切正常, 代码块中最后一个表达式就是结果。如果捕获到了一个异常,相应 catch 代码块中最后一个表达式就是结果。</p>
fun readNumber(reader: BufferedReader) { val number = try {
Integer.parseInt(reader.readLine())
} catch (e: NumberFormatException) {
null
}
}
<p>在学完以上这些内容后你就已经能够使用 Kotlin 进行开发了, 并且能够看懂使用 Kotlin 编写的代码了。希望 Kotlin 能够提高你的工作效率,节省你的时间,帮助你写出更简洁,安全,实用性高的代码。</p>
小结
- fun 关键字用来声明函数。 val 和 var关键字用来声明只读变量和可变变量
- 字符串模板帮助你避免繁琐的字符串连接。在变量名称前加上 $ 前缀或者使用 ${ }包围一个表达式,来吧值注入到字符串中。
- 值对象在 Kotlin 中用更简洁的方式表示。
- 熟悉的if现在是带返回值得表达式。
- when表达式类似于Java中的 switch 语句但是功能要更加强大。
- 在检查过变量类型后不需要显示的进行类型转换,编译器会使用智能类型转换。
- for,while,do-while循环和 Java类似,但是 for 循环更加方便,特别是在迭代 map 的时候,又或者是迭代集合需要下标的时候。
- 简洁的语法1..5会创建一个区间。区间和数列允许 Kotlin 在 for 循环中用同意的语法和同一套抽象机制,并且还可以使用 in 运算符和 !in 运算符来检查值是否属于某个区间。
- Kotlin 中的异常处理和Java非常相似,除了 Kotlin 不要求声明函数可以抛出的异常 ,Kotlin 中 try 是表达式。
内容参考自:
- https://www.kotlincn.net/ Kotlin 中文官网
- 《Kotlin in Action》
创建了一个 Kotlin 学习交流群有兴趣的同学可以加群一起交流学习
以上是 Kotlin系列之Kotlin基础 的全部内容, 来源链接: utcz.com/z/512447.html