基础语法
1.变量申明
java9
中采用了静态类型推断的特性,用var
和val
来申明变量,kotlin
也是如此:
//等价于 int a = 10
var a = 10
//等价于 final int b = 10
val b = 10
静态类型推断本质上就是一个语法糖,在java这种强类型语言当中,当把变量创建出来,则变量的类型就已经固定,和动态类型推断的最大区别就在于这个变量能不能被赋上其他类型的值,比如下面我们先创建a为10,明显是一个数字类型,但是在赋值为string
,则报错,因为a值得类型,已经在var a = 10
时已经确定.
如果我们一定要加一个类型,则可以采用下面这种方式
var a:Int = 10
val b:Long = 10
那这样就和java中申明变量,int a = 10;
无太大区别,反而多了一个关键字,但是却可以大大提高可读性,因为往往在一长串代码中,要知道当前变量类型,要借助idea
的提示,还是很扯淡的.
2.基本数据类型
在kotlin
中的基本数据类型有以下几种,数值
,布尔
,字符
,字符串
和数组
.
2.1 数值
2.1.1 整数类型
Type | Size (bits) | Min value | Max value |
---|---|---|---|
Byte | 8 | -128 | 127 |
Short | 16 | -32768 | 32767 |
Int | 32 | -2,147,483,648 (-231) | 2,147,483,647 (231 - 1) |
Long | 64 | -9,223,372,036,854,775,808 (-263) | 9,223,372,036,854,775,807 (263 - 1) |
这几个类型和java
并无太大的区别,在kotlin
当中移除了int long
等基础类型,取而代之的是封装类型.
2.1.2 浮点型
Type | Size (bits) | Significant bits | Exponent bits | Decimal digits |
---|---|---|---|---|
Float | 32 | 24 | 8 | 6-7 |
Double | 64 | 53 | 11 | 15-16 |
2.2 布尔类型
布尔类型和java的无异,也是true
和false
,支持&,|,!
等常规操作,也支持&&,||
等短路操作.
2.3 字符
字符类型和java
的有点区别,我们再java
中,可以直接把数字赋给char
,比如把97赋给char,代表'a'
这个字符:
char a = 97;
这种操作在java中不再被允许,字符类型的申明必须是字符:
var a:Char = 'a';
另外,因为kotlin
中的Int
是封装类型,字符想强转为Int
也不在被支持,可以通过字符的api
,digitToInt()
来实现转换为Int
的需求.
2.4 字符串
字符串多年的媳妇熬成婆,在kotlin
中终于被定义为了基本数据类型.
申明一个字符串,并输出打印每一个字符:
var str = "Pantheon"
for (c in str) {
println(c)
}
在java高本版当中,字符串支持格式化,在kotlin
当中也是支持的,可见语言的特性都是一家亲,大家互相抄抄而已,比如定义一个json
类型的字符串:
var str = """
{
"name":"Pantheon",
"age":20
}
"""
它的规则就是,申明的字符串必须是"""
开头和结尾,串里面不用在处理烦人的转义和换行,可以像定义一样,获得字符串的格式,所见即所得,非常的哈拉少.
字符串的另外一个特性就是字符串模板,我们想象一下,在java当中,如果我们要拼接一段字符串,应该怎么做?
int a = 10;
String s = "拼接的a值是:"+a+".";
这种方式非常的不哈拉少,所以在kotlin中,出现了字符串模板的功能,看看kotlin中如何实现上述的需求:
var a = 10
val s = "拼接的a值是:${a}."
如果只有一个参数{}
也可以省略,感觉是不是要比java优雅的多?
2.5 数组
数组在kotlin中用类型Array
来表示,创建一个数组由静态方法arrayOf
来实现,
也可以通过内置的类型函数来创建一些指定类型的数组.
var arr = arrayOf(1,2,3)
val intArray = IntArray(1)
val longArray = LongArray(1)
val byteArray = ByteArray(1)
intArray[10] = 10
上述代码中,我们指定intArray
大小为1个,但是给它第十个index塞了个值,所以会报数组越界异常,这和java没什么区别:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
at cn.pmj.starter.basic.types.NumberTypeKt.main(NumberType.kt:18)
at cn.pmj.starter.basic.types.NumberTypeKt.main(NumberType.kt)
数组其中创建的一些api结合函数一起使用,会有一些匪夷所思的语法,比如
var arr = IntArray(5) { it * 1 }
arr.forEach { println(it) }
它所表达的意思是创建一个大小为5的数组,然后每一个索引位置的值等于该索引的值:
0
1
2
3
4
这里的{it * 1}
其实是一个lambada函数,it是指参数的名称(也就是index),如果lambada参数只有一个,这是默认名,所以最终创建出来的数组的值就等于该值的索引值.
3 控制语句
3.1 if
if语句和java并无太大的差别,如
var a = 10;
if (a > 10)
print("a > 10")
else
println("a <= 10")
唯一比较大的区别,if语句块中结果赋值给某一个变量,需要注意的是语句块中的值需要单独占一行
var a = 10
var b = 20
var max = if (a > b) {
print("a > b")
a
}else 10
这种写法可以实现java中三元运算符的效果,在kotlin中是没有三元运算符的
var max = if(a > b) a else b
3.2 when
kotlin中取消了switch语句,取而代之的是when语句,来实现多分支判断
var a = 10
when(a){
1 -> print("a == 1")
2 -> print("a == 2")
else -> print("a == $a")
}
我们反编译看下when语句到底是什么鬼:
int a = 10;
String var1;
switch(a) {
case 1:
var1 = "a == 1";
System.out.print(var1);
break;
case 2:
var1 = "a == 2";
System.out.print(var1);
break;
default:
var1 = "a == " + a;
System.out.print(var1);
}
可以看到when语句其实就是switch语句的变体,用箭头函数表达case,并且加上了break
关键字,语法糖的游戏。
when语句也可以配合其他的语法糖结合,比如枚举,in关键字,多类型case:
enum class Color {
RED,GREEN,YELLOW
}
var color = Color.RED;
when(color){
Color.RED,Color.GREEN -> print("color is red or green")
else -> print("color is YELLOW")
}
var num = 10;
when(num){
in 1..10 -> print("num is between 1 and 10")
!in 10..20 -> println("num is not in (10 - 20)")
else -> println("out of range")
}
甚至,我们可以把判断条件写到箭头函数前面
var a = 10;
var b = 20
when{
a in 1.. 10 -> print("a in 1..10")
b in 2..30 -> print("b in 2..30")
else -> println("a < 10")
}
但是这种情况,因为判断语句不再互斥,所以从上往下执行,只要有一个满足条件,则退出判断,不再往下执行
3.3 for
for循环语句,它的语法和C#
是一样的,和java有点出入,但是不大,比如遍历一个list:
for (i in list) {
print(i)
}
再比如,从1开始遍历到100,步长为2,这种写法就又点像python
for (i in 1..100 step 2) {
println(i)
}
所以说,天下语法一大抄,抄来抄去,都差不多。
3.4 while & do..while
while和do..while和java语言没有什么太大差别,不做过多介绍:
var i = 10;
while (i > 10) {
i--;
}
do {
i--;
} while (i > 10)
3.5 continue & break & return
这三个关键字,都是用来控制流程的跳转,在kotlin中的语意和java中的无太大区别,基础用法不做介绍,主要介绍下,return在lambda里面的用法.
首先需要明确一个规则,如果当前调用的函数非inline
函数,是不可以用return
的,只能有continue
和break
var func = {a:Int-> if (a == 10) return}
对于返回unit
的lambda.此时不需要申明写出return
,不用任何返回即可,上面这个代码会报错,ide会提示return
语句无法在lambda中使用,如果遇到有返回值的lambada呢?可以像下例代码一样,将返回值单独写在最后一行,代表是return语句.
var func = {a:Int->
if (a == 10)
12
else
0
}
var rs = func(10)
println(rs)
情况在inline
函数中发生了转变,inline
函数在编译期的时候会对代码做拷贝优化
inline fun print(list:List<Int>,pre: () -> Unit){
pre()
list.forEach{ println(it)}
}
fun main() {
print(mutableListOf(1, 2, 3)) { println("begin") }
}
比如这行代码,它在生成字节码时,会最终变成这样子:
fun main(){
var list = mutableListOf(1, 2, 3)
println("begin")
//foreach也是一个inline函数
for(var item in list){
print(item)
}
}
在没有返回值的时候,一点问题都没有,如果有返回值了呢?因为存在字节码拷贝的问题,所以需要告诉编译器,我的代码是return
的哪个语句,所以出现了定义label
的return
用法.
var getTen = getTen@fun (list:List<Int>):Int{
list.forEach{
if (it == 10)
return@getTen it
}
return 0;
}
val ten = getTen(mutableListOf(0, 10, 2,19))
println(ten)
我们对这个getTen
函数定义了一个标签,最终return
出来的返回值代表的是getTen
函数的返回值,当然对于一些常见的函数也内置了一些label
,比如
var getTen = getTen@fun (list:List<Int>):Int{
list.forEach{
println(it)
if (it == 10)
return@forEach
}
return 0;
}
val ten = getTen(mutableListOf(0, 2,10, 19))
----------------
0
2
10
19
虽然我们定义了getTen
,但是我们的return
却是forEach
这个函数,此时的return
已不在是返回的意思,而是continue
的意思.