本文收录于 kotlin入门潜修专题系列,欢迎学习交流。
创作不易,如有转载,还请备注。
基础语法
基本类型
学习一门新的语言首先要学习其基本类型,这是一门语言世界的基石。
在kotlin中,一切都是以对象的类型存在,这点与java不同。但实际上有很多类型在runtime的时候都会被替换为原生类型,只不过对于用户而言都是普通的对象类型而已。kotlin中的基本类型可以分为几个大部分:numbers(数字)、characters(字符)、booleans(布尔值)、arrays(数组)以及strings(字符串)。这些类型由于都是对象类型,对象的共有特性都会支持,比如可以赋值为null等。
数字类型(Numbers)
kotlin中对数字的处理和java很像,但是依然有些许区别,比如kotlin中不再有隐式转换、常量表示有些时候也和java中不同(如支持下划线分割)等等。kotlin内置有6种基本数字类型(注意kotlin中字符不是数字),如下所示:
类型 | 位数 |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
数字字面常量
kotlin中的数字字面常量有三种类型:
- 十进制:表示方式同java,如100,1000等,当使用特定类型时加上后缀标识符即可,如Long类型 124L,Float类型:123.0F。对于小数如果不加后缀F(或f,大小写都可以)则默认为Double类型。
- 十六进制: 如0xFF
- 二进制: 如0b00110011
注意,kotlin中不支持八进制数字字面常量。
kotlin为了提高字面常量的可读性,支持对数字进行下划线分割(1.1开始),如下所示:
val intNum = 1_000
val longNum = 1234_5678_9012_3456L
val hexBytes = 0xFF_FF_FF_FF
val bytes = 0b11111111_00000000_10101010_01010101
在java中,数字一般都是以原生类型存在jvm中的,只有少数情况下会使用装箱操作,比如使用泛型的时候。由于kotlin中一切即对象,所以这点与java不同,即所有的数字都要用对象类型来表示。
接着来看下kotlin中的比较操作,java中整型数字的比较示例如下:
Integer i = new Integer(10000);
Integer j = i;
Integer k = i;
Integer h = new Integer(10000);
System.out.println(j == k); //打印 'true'
System.out.println(j == h); //打印 'false'
System.out.println(j.equals(k)); //打印 'true'
上面代码大家应该都很清楚,在java中==表示的是对两个对象的地址进行比较,而equals表示的是对内容进行比较。
再来看下kotlin中的整型数字如何进行比较:
val a: Int = 1000
println(a === a) // 打印 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
println(boxedA == anotherBoxedA) //打印 'true'
println(boxedA === anotherBoxedA) //打印 'true',这里和上一个语句的区别:注意区分“==”和“===”操作符。
println(boxedA?.equals(anotherBoxedA))//打印 'true',
上面代码提到了三个比较方法:==、===和equals,那么三者有什么区别呢?
首先看下最简单的euqals方法,这个和java中的equals方法是一致的,都是用于内容比较,而不是比较地址;
然后再来看下双等号(==)所表达的含义,其实在kotlin中,双等号就是个操作符,其对应的实现就是equals方法,如下所示:
public open operator fun equals(other: Any?): Boolean
也就是说当比较对象都不为null时,==和equals方法的功能是一模一样的;
最后再来看下三等号(===),这个在kotlin中,实际上比较的是两个对象的地址,其功能和java中的双等号(==)一致。
因此,在进行内容比较的时候建议使用双等号(==)或者equals方法,而在进行地址比较的时候则使用三等号(===)。
数字类型显示转换
kotlin不在像java那样可以进行隐式转换(是指原生数据类型,如int可以隐式赋值为long),在kotlin中必须要进行显示转换:
// 下面的代码在kotlin中是无法变编译的
val a: Int? = 1 // 包裹类型(java.lang.Integer)
val b: Long? = a // 这里即报错,两个类型不一致,一个是Int?,一个是Long?
解决方案及时采用显示转换
val a: Int? = 1
val b: Long? = a?.toLong()
kotlin中,每一个数字类型都对应有这样一个方法,如下所示:
— toByte(): Byte
— toShort(): Short
— toInt(): Int
— toLong(): Long
— toFloat(): Float
— toDouble(): Double
— toChar(): Char
如果不允许隐式转换,那么用户在计算表达式中有两种数字类型怎么解决?如下所示:
val l = 1L + 3 // Long + Int => Long
上面代码是允许出现的,这是因为kotlin会根据上下文环境和操作符(这里是+)的重载自动推断相应类型。
位操作符
java中的位操作符有左移(<<)、右移(>>)等等,kotlin中不在支持这些操作符,转而用方法替代:
val x = (1 shl 2) and 0x01
这点特性确实很赞,毕竟不用再去区分<<到底是左移还是右移了。
kotlin提供了以下位操作相关方法,但支持Int和Long类型。如下所示:
— shl(bits) – 有符号左移
— shr(bits) – 有符号右移
— ushr(bits) – 无符号右移
— and(bits) – 与操作
— or(bits) – 或操作
— xor(bits) – 异或操作
— inv() – 倒置操作
浮点数比较
— 是否相等: a == b and a != b
— 大小比较: a < b, a > b, a <= b, a >= b
— 范围操作符: a..b, x in a..b, x !in a..b
其中是否相等的比较和大小比较和java中一致,但是kotlin新增了范围操作符,用于检测某个数字是否在指定的范围内。这点还是很方便的。
val i = 1.1
if (i in 0..2) {
println(i.toString().plus(" in 0-2"))//打印 '1.1 in 0-2'
}
以上是对标准浮点数类型的比较,然而对于非标准浮点数怎么进行比较?比如对包含有浮点数的list进行排序,这个时候kotlin和java一样,会调用Float或者Double的equals、compareTo进行比较。
kotlin对浮点数字类型还定义了一个NaN值,这个值表示当前比较对象并不是数字,同时kotlin还对0进行了正负区分,这在调用compareTo的时候会产生和想象中不同的效果,具体事例如下:
val i :Double = -0.0
val j :Double = 0.0
println(i == j)//打印 'true'
println(i.compareTo(j))//打印 '-1',表示 i < j
println(Double.NaN == Double.NaN)//打印 'false'
println(Double.NaN.compareTo(Double.NaN))//打印 '0',表示 NaN = NaN
println(Double.NaN.compareTo(Double.POSITIVE_INFINITY))//打印 '1',表示 NaN > 无穷大
字符类型(Characters)
字符类型的关键字是Char。
kotlin中的字符类型是Char,与java中区别就是kotlin中的字符无法直接作为数字来表示,示例如下:
java代码示例:
char c = 'a';
int i = c;
System.out.println(i);//打印 '97'
kotlin代码示例:
val c :Char = 'a'
val i :Int = c//报错,c是Char类型,Int是整型,无法赋值
但是我们可以显示的对Char进行转换,如下所示:
val c :Char = 'a'
val i :Int = c.toInt()
println(i)//打印 '97'
布尔类型(Booleans)
布尔类型的关键字是:Boolean
同java一样,Boolean类型数据有两种值即true和false,同样,也支持一下操作符:
— || – 或操作,支持截断
— && – 与操作,支持截断
— ! 非操作
上述中的支持截断的意思是在&&或者||操作的时候只要第一个不满足或满足条件就不再执行&&或者||之后的语句,示例如下:
@JvmStatic fun main(args: Array<String>) {
if(getFalse() && getTrue()){
}
println("&& test end ...")
if (getTrue() || getFalse()){
}
println("|| test end")
}
fun getTrue():Boolean {
System.out.println("getTrue execute...")
return true
}
fun getFalse():Boolean {
System.out.println("getFalse execute...")
return false
}
以上打印为结果为
getFalse execute...
&& test end ...
getTrue execute...
|| test end
即在&&测试中,只要第一个(getFalse)返回了false,就不会执行下一个操作(即getTrue),||也是如此。
数组(Arrays)
数组的关键字为:Array
kotlin以Array为关键字来表示数组,提供诸如get、set等方法以及size属性等等,同时也支持操作符[],示例如下:
val arr1: Array<Int> = arrayOf(1, 2, 3)
println(intArr[0])//打印’1‘
arr1.forEach(::println)//打印'1 2 3'
val arr2: Array<Int?> = arrayOfNulls(3)
arr2.forEach(::println)//打印'null null null'
val arr3: Array<String> = Array(5, { i -> (i + i).toString() })
arr3.forEach(::println)//打印'0 2 4 6 8'
需要注意的是Array只有一个构造方法,即上面代码中的最后一个构建数组的示例,除此之外没有其他构造方法。该构造方法第一个参数为数组大小,第二个参数接收一个方法,进行数组元素的初始化。
kotlin中的数组是不可变的,无法像java那样进行向上类型的赋值,如下所示:
java代码示例
Object [] str = new String[]{"1","2"};//java中是可以这样赋值
kotlin代码示例
val intArr: Array<Int> = arrayOf(1,3)
val objs:Array<Any> = intArr//编译错误,无法将Array<Int>赋值于Array<Any>
val objs:Array<out Any> = intArr//编译正确,这里的out关键字表示只要继承于Any的任何类型都可以转。
注意上述代码中,Any是kotlin中所有类的基类,相当于java中的Object。
除此之外,kotlin还提供了特定类型数组,如ByteArray, ShortArray, IntArray等等,用户可以直接使用这些数组类型以满足需求。
字符串类型(Strings)
kotlin中字符串类型关键字为String
在kotlin中String是不可变的,同java一样,kotlin对String提供了很丰富的支持。具体事例如下:
val str: String = "hello"
println(str[0])//打印'h',标明支持[]操作符
for (c in str) {//支持for ... in遍历
print(c.plus(" "))//打印'h e l l o '
}
val str2 = str + " word"//支持+运算符
println(str2)//打印’hello word‘
字符串字面常量
kotlin中,字符串字面常量有两种,一种是包含转义字符的字符串,一种是原生字符串(raw string),示例如下:
val str: String = "hello, world!\n"
print(str)//打印 'hello,world'
val text: String = """
for (c in "text")
print(c)
"""
println(text)//打印 原生格式字符串,见下文
val text2: String = """
|first line
|second line"""
println(text2)//打印原格式字符串
println(text2.trimMargin())//打印去掉|之前的所有字符串
上述代码,text会被保持原来格式的打印出来,而text2中有个|字符,这个是kotlin中默认作为margin前缀的字符,当调用trimMargin的时候,会默认清楚|之前的所有空格,并保持对齐,朋友们可自行试验。
字符串模板
kotlin支持字符模板,简单的说就是可以在字符串中解析变量,示例如下:
val s = "abcd"
println("$s.length is ${s.length}") //打印 'abcd.length is 4'
val unit = '$'
val price = """ ${unit}1.11"""//这里可以看出字符串模板也支持row string
println(price)//打印 $1.11
至此我们就将kotlin中的基本类型讲完了。
需要说明下,上文中有很多写法可能有朋友暂时还看不明白(比如泛型、运算符重载等等)。不过,没有关系,这些内容都会在后面文章中一一阐述,等看完本系列文章,一切就迎刃而解了。看本篇文章的时候只需关注基础语法即可。