作为Google【官方指定的干儿子】,Kotlin语言今年受到了越来越多的重视。无论是开发Android还是后台程序,这门语言以后一定是大有可为。由于相关的文章很多,那我们不多介绍这门语言是怎么来的。就让我们慢慢来,一点一点的去品味Kotlin。
第一章,一起来看看这门语言的基本数据类型吧。
开始之前,我们需要明确一点:
PS:
1、强类型:强类型语言的编译器引入了较多的类型检查限制,因此在运行时不会发生未经明确(显示转换)类型转换的类型转换。常见的语言中,Java就是强类型语言,而JavaScript则是弱类型语言。
2、静态类型:根据类型检查在编译时期还是运行时期,我们把语言分为静态类型以及动态类型。静态语言是基于编译器来确保语言类型的安全。
3、显式类型:根据变量名是否需要显式的给出声明,我们可以将语言分为显式类型以及隐式类型。kotlin是显式类型的语言,与此同时又因为有【类型推断】的作用所以可以看做是支持隐式类型。
kotlin中,var符号表示的事变量,可以多次重复赋值。
而val表示的是“常量”,但这并不是通俗意义上的常量。本质上val也是变量,但一次赋值之后,不能再次修改,只能作为【只读变量】。
kotlin中万物皆对象,所有类型都是引用类型。所有的类都有一个超类:Any。
官方文档是这么说的:
/**
* The root of the Kotlin class hierarchy. Every Kotlin class has [Any] as a superclass.
*/
这个类只有三个方法:
public open operator fun equals(other: Any?): Boolean
public open fun hashCode(): Int
public open fun toString(): String
如果类声明中,没有指明超类,则默认为Any。
kotlin中的Any映射为java中的java.lang.Object.
特别注意的一点:java中Object只是所有引用类型的超类,而基本类型int、long等不包含在内。而kotlin中,Any是一切的超类。
kotlin中常见的数字类型分为以下6种:
方法名 | 转换类型 | 补充 |
---|---|---|
Byte | 8位 | 1字节 |
Short | 16位 | 2字节 |
Int | 32位 | 4字节 |
Long | 64位 | 8字节 |
Float | 32位 | 4字节 |
Double | 64位 | 8字节 |
根据名称可以看出基本和java中的相关定义相近。
kotlin支持二进制、十进制、十六进制;
但【不】支持八进制
与此同时,kotlin在数值类型的表示方式上也支持下划线,如:
val Million = 1000_000_000
val tmp = 123_456_789L
val demo = 0xDD_FF_EE
kotlin中的数字类型与java很是相近,但有一点不同:
kotlin没有java语言的隐式变换(如byte->short->int->long 等)。
因此,java中的如下代码,在kotlin中是无法编译的:
short a = 1;
int b = a;
当然,在kotlin中应该这样描述:
val a : Int = 1
val b : Long = a
上述代码同样是无法通过编译的!
因此,遇到类型转换我们就应该考虑一下【显式转换】:
java中,我们一般会这样进行数值类型的显式转换:
int a = 1;
...
Long tmp = (long)a;
而kotlin中,我们可以这样:
val a : Int = 1
val b : a.toLong()
相关的方法汇总如下:
方法名 | 转换类型 |
---|---|
toByte() | Byte |
toShort() | Short |
toInt() | Int |
toLong() | Long |
toFloat() | Float |
toDouble() | Double |
toChar() | Char |
这里需要注意一点的是:
与java不同,kotlin中Char类型仅仅表示字符,不能再被直接当做数字。 因此,Char类型的变量必须在单引号之间表示:’变量’。(CSDN博客这里显示有误。。。)
一段简单的java代码如下:
char a = 'c';
System.out.println(a+1);
此时打印的结果为【数字】100。
而作为对比,在kotlin中:
val a : Char = 'c'
println(a+1)
上述代码的打印结果为【字符】d
因此我们要明确:Kotlin中 Char类型由于不是直接当数值用,所以最后返回依旧是Char型。
kotlin中并没有单纯的数值,在我们定义每一个数值的时候,kotlin编译器都在默默的为我们将定义的数值封装成一个对象,以最大限度的解决空指针异常的问题。因此kotlin中数值的比较除了我们常见的 == 号之外还多了 === 。
当然,根据面向对象的定义也可以快速的了解:
符号名 | 作用 | 否定形式 |
---|---|---|
== | 【结构相等】比较两个具体的【数值的大小】是否相同 | != |
=== | 【引用相等】比较两个对象【在内存的存储地址】是否相同 | !== |
ps:
编译器中,== 等同于equal()方法。
举个栗子来看:
fun main(args: Array){
val a : Int = 10
println(a === a) //此处返回的值为true
val b : Int = a
println(a === b) //此处返回的值为true,因为赋值操作是内存地址赋值,而内存地址相同,其中的数字值自然相同
println(a == b) //此处返回的值为true,原因同上
}
【☆☆☆】考虑到装箱问题:数字装箱不保留【同一性】,但保留数值的【相等性】。
val a: Int = 200
println(a === a) //此处返回的值为true
val b :Int ? = a
println(a === b) //此处返回的值为false,因为这是两个对象,内存地址不相同
println(a == b) //此处返回的值为true,数值相同
当然还有一个特殊情况,考虑到JVM虚拟机的常量池特性:
val a: Int = 20
println(a === a) //此处返回的值为true
val b :Int ? = 20
println(a === b) //此处返回的值为true
println(a == b) //此处返回的值为true,数值相同
PS:
【1】JVM虚拟机中维护着有符号整形常量池(-128,127),在这个范围里的数值都会直接使用常量池的内存地址,所以这个范围内的数值装箱后比较内存地址依旧是相等的。(常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References))
【2】kotlin中,?表示这个对象可能为空。因此这一句“val b :Int ? = a”
表示的意思除了普通的数字赋值外,还多了一层含义:如果被赋值对象为空,则开辟内存创建新的对象。
在java中,移位操作的表示非常简洁,直接用符号即可表示:
符号名称 | 作用 |
---|---|
<< | 左移位操作 |
*>> | 右移位操作(此处*为显示效果) |
*>>> | 无符号右移位 |
而在kotlin中,则没有这些简单的符号表示,取而代之的是相应的移位操作方法:
符号名称 | 作用 |
---|---|
shl(bits) | 左移位 |
shr(bits) | 右移位 |
ushr(bits) | 无符号右移位 |
and(bits) | 与操作 |
or(bits) | 或操作 |
xor(bits) | 异或操作 |
inv() | 反向操作 |
先来看一段代码:
val a = 10L + 1
很明显,上述代码中,+ 左右两个数分别为Long类型与Int类型。
那么问题来了,我们前面说过,kotlin中并不支持数字类型的隐式转换,而且上面表达式中的数字1并没有显示拓宽为Long类型,为何却准确无误呢???
答案很简答,那就是kotlin中对于符号“+”的重载。
看一看到,源码中有很多重载的方法。
当我们执行:val a = 10L + 1
实质上是:
val a = 10L.plus(1)
值得注意的一点是:XX.plus()方法中传入的形参只能为数值类型!
kotlin中的基本字符为Char类型,相关的知识点我们已经在前文讨论过。
布尔用 Boolean 类型表示,它有两个值:true 和 false。
若需要可空引用布尔会被装箱。
kotlin中字符串是String类型。
与java类似,kotlin中的String同样是由final修饰的,即不可被继承。我们一起来看一些新的特性。
val string = """
line 1
line 2
lin3 4
"""
我们可以借助与 trimMargin() 发放做到去除掉每行的前置空格。
val string = """
|line 1
|line 2
|lin3 4
""".trimMargin("|")
这里有一点需要注意:trimMargin()方法需要传入一个String类型的参数作为删除前置空格并进行每行分割的标志Flag,我们在这里选用了”|”。
在kotlin中,字符串可以包含字符串模板用来表示前面用的到的一段代码。kotlin中规定,所有的字符串模板必须以美元符号”$”开头。
val a = "temp"
val q = "$a.length():"
println(q+a.length)
原生字符串和转义字符串内部都支持模板。
当我们需要对一个字符串的某一位取值时,java中会用到:
"abc".charAt(i);
而在kotlin中,我们可以简单的用索引运算符即可:
val s = "abc"
s[0]//结果为a
for (s in "12345"){println(s)}
kotlin中的数组不是new出来的而是用了别的表示方法:
【1】fun_1
val a = arrayOf(1,2,3) //1,2,3
【2】fun_2 工厂函数
val b = Array(3, { i -> (i * 2) })//0,2,4
kotlin中的数组支持多类型:
val arr = arrayOf(1,2,true,"abc")
for(c in arr){println(c)}
上述代码的打印结果为:
注意:在这里kotlin编译器自动将数组升级为java.lang.Object类型了。
除此之外在新建空数组时,我们必须显示地知名数组元素的类型:
val b = arrayOfNulls(10)//错误
val b = arrayOfNulls(10)//正确
注意:kotlin中数组不是形变的,即我们不能将Array赋值给Array。