1.空值检查(空安全)
Kotlin 是空指针安全的,如果一个变量可能为空,必须在定义的时候显式地指定出来(在类型后面加上“?”)。
如
var a : String ? = "666"
a = null //可以编译通过
var b : String = "666"
b = null //无法编译通过,因为这里没有显式指定b可以为空
Kotlin中还有一些涉及到空安全的操作符:
?.
如果不为空,则调用 例如:b?.length()
如果b不为空,则返回b的长度,否则返回空
?:
如果为空,则…(Elvis 操作符) 例如:val l = b.length ?: -1
如果b.length
为空,则返回-1,如果不为空则返回b.length
!!
这会返回一个非空的 b 或者抛出一个 b 为空的 NPE 例如: val l = b !!.length()
as?
空安全转型,普通的转换可能产生 ClassCastException 异常。另一个选择就是使用安全转换,如果不成功就返回空:val aInt: Int? = a as? Int
2.智能类型转换
在判断一个对象是否为一个类的实例的时候,可以使用is
关键字,这与 Java 中的instanceof
关键字类似。如果在前面某个变量已经使用is
进行了判断,那么在后面的语句块中,该变量将会被自动转型,例如:
Kotlin:
fun getName(obj :Any) : String?{
if (obj is String){
//在这个语句块中,obj将会被自动转换成String类型,因此可以直接返回obj
return obj
}
return null
}
而在Java中,则没有这么智能:
Java:
String getName(Object obj) {
if (obj instanceof String) {
return (String) obj;
}
return null;
}
3.区间表达式(in
和 ..
)
区间表达式是通过 rangeTo 函数形成的。rangeTo 函数拥有形如 .. 的操作符,该操作符是用 in 和 !in 实现的。判断一个对象是否在在某一个区间内的时候可以使用in
关键字,常常用于if
或者for
的条件,在for中使用前面已经讲了,下面就先来看if:
if:
fun rangesIf(a : Int){
//使用闭包进行数据的初始化
val ints : IntArray = IntArray(10,{i -> i})
//如果a在0到10之间,则打印"I am in"
if (a in 0..10){
println("I am in")
}
//如果a不在0到10之间,则打印"I am not in"
if (a !in 0..10){
println("I am not in")
}
//如果a在数组ints中,则打印"I am in"
if (a in ints){
println("I am in")
}
}
此外,在for循环中,如果想从大的数字迭代到小的数字,也可以使用downTo
函数。
for:
for (i in 4 downTo 1) {
println(i)
}
结果是:
4
3
2
1
这里还可以使用step
指定一个步长,用于指定每个多少个单位迭代一次:
for (i in 1..4 step 2){
println(i)
}
结果是:
1
3
4.单表达式函数
如果一个函数内部只包含一个ruturn语句,以后直接用单表式来表示,如:
fun getNum() : Int{
return 6
}
等价于:
fun getNum() = 6
5.对象表达式
对象表达式的概念就类似于Java中的匿名内部类,主要用于我们想要创建一个对当前类有一点小修改的对象,但不想重新声明一个子类时。
通过object :
创建:
window.addMouseListener(object: MouseAdapter () {
override fun mouseClicked(e: MouseEvent) {
//...
}
})
有时候我们只是需要一个没有父类的对象,可以这样写:
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
值得注意的是,Java中的匿名内部类只能访问被final修饰的外部变量(即不可变的变量),而Kotlin中却可以访问var修饰的变量(可变的变量)。
Java:
int b = 6;
window.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
b++;//报错,匿名内部类中的变量是final的,不能自加
}
});
Kotlin:
var b : Int = 6
window.addMouseListener(object : MouseAdapter(){
override fun mouseClicked(e: MouseEvent?) {
b++
}
})
6.对象声明
如果在 object
后面添加了一个对象名,如 object Hello
,这就表示一个对象声明。
在声明的时候不能作为值赋给变量,不过声明完成后就可以赋值了:
//报错,不能在对象声明的时候同时赋值给变量
var myObject = object HelloWorld {
fun sayHello(){
println("Hello world")
}
}
下面这种写法则是正确的:
object HelloWorld {
fun sayHello(){
println("Hello world")
}
}
var myObject = HelloWorld
Kotlin中的单例模式就是用对象声明来构造的,不过这种单例其实是对应于Java中的最简单的单例–饿汉式
Java:
public class Singleton {
public static final Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getINSTANCE() {
return INSTANCE;
}
}
调用:Singleton.getINSTANCE().say();
Kotlin:
object Singleton {
fun say(){
println("Hello")
}
}
调用:Singleton.say()
7.伴随对象
Kotlin中没有static
关键字,因此如果想实现类似于Java中的静态方法类似的功能,就要借助伴随对象,伴随对象使用companion
关键字标记对象声明,它里面的方法就类似于Java中的静态方法:
class MyClazz {
companion object Factory {
fun say(){
println("Hello")
}
}
}
调用:MyClazz.say()
8.解构声明(Destructuring Declarations)
Kotlin可以通过解构声明将一个对象解构成多个变量。例如:
data class Person(val name : String ,val age : Int ,val sex : String)
val (name, age, sex) = Person("Solinzon",21,"male")
println("I am $name , a $age year-old and $sex man")
结果是:
I am Solinzon , a 21 year-old and male man
这样一来就可以省略我们单独去定义name
, age
, sex
这三个变量的操作。
Kotlin中最经典的解构声明的例子就是遍历map的时候:
val map = mutableMapOf<Int,String>()
for (i in 0..3) map.put(i,"I am element $i") //初始化一个map
for ((key,value) in map){
println("$key -> $value")
}
结果是:
0 -> I am element 0
1 -> I am element 1
2 -> I am element 2
3 -> I am element 3
9.相等
在Kotlin中的相等分为两种:参照相等(Referential equality)和 结构相等(Structural equality)
参照相等用===
表示,它指的是两个等号两边的两个变量指向的是同一个对象。
结构相等用表示==
,它指的是两个对象的内容一样。
val a = Person("Solinzon",21,"male")
val b = Person("Solinzon",21,"male")
println(a === b)//没有指向同一个对象,返回flase
println(a == b)//内容相等,返回true
结果是:
flase
true