• var 定义变量
  • val 定义常量
  • Kotlin 支持类型自动推导
    var i=1 这种形式也正常

  • is 进行类型判断
  • as 进行类型转换,可能抛出异常
  • as? 进行安全类型转换

在 Kotlin 中,

  • 三个等号 === 表示比较对象地址,
  • 两个等号 == 表示比较两个值大小。

// 下面两句两个语句等价 a == b a?.equals(b) ?: (b === null) // 如果 a 不等于 null,则通过 equals 判断 a、b 的结构是否相等 // 如果 a 等于 null,则判断 b 是不是也等于 null

print(a === b) // 判断 a、b 是不是同一个对象

var ages2: String? = "adc"
ages2 ?: age.also {
    println("also 执行了")
    ages2 = it
}
print(ages2)

//等价于
var ages2: String? = null
if (ages2.isNullOrEmpty()) {
    age.also {
        println("also 执行了")
        ages2 = it
    }
}
print(ages2)

委托 (Delegation)

使用by 关键字

by lazy 实现”懒加载“

by Delegates.observable 实现”观察者模式”的变量

class User{
  // 为 name 这个变量添加观察者,每次 name 改变的时候,都会执行括号内的代码
    var name:String by Delegates.observable("no name"){
        property, oldValue, newValue ->
        println("name 改变了: $oldValue -> $newValue")
    }
}

lazy只适用于val对象,对于var对象,需要使用lateinit,原理是类似的,只是代码需要这样写

lateinit var bottomNavigation: BottomNavigationView

// 通过 by 关键字,将 lazyValue 属性委托给 lazy {} 里面的实现
val lazyValue: String by lazy {
    val result = compute()
    println("computed!")
    result
}

Kotlin 里单引号定义的是单个字符,双引号才是定义字符串。

使用Java类

val intent = Intent(this, MainActivity::class.java) //需要用::来使用Java类,注意是两个“:”

object在Kotlin中用来表示单例,Kotlin用Any来表示所有类的基类

::

  1. 一个方法当做一个参数,传递到另一个方法中进行使用,通俗来讲就是引用一个方法
seq.take(5).forEach {
        log(it)
    }
//遍历整个集合
相当于
seq.take(5).forEach(::log)

字符串模板

  • $ 表示一个变量名或者变量值
  • $varName 表示变量值
  • ${varName.fun()} 表示变量的方法返回值:
var a = 1;
var s1 = "s1 is $a" //s1 is 1
a = 2;
var s2 = "${s1.replace("is", "was")},but now is $a" //s1 was 1,but now is 2

NULL检查机制

两种处理方式

  • 字段后面加!! ,像java一样抛出空指针异常
  • 字段后面加?,不做处理,返回值为null或者配合?:做空判断处理
//类型后面加?表示可为空
var age: String? = "23"
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1

注意: 当一个引用可能为 null 值时, 对应的类型声明必须明确地标记为可为 null。

Kotlin 中没有基础数据类型,只有封装的数字类型

你每定义的一个变量,其实 Kotlin 帮你封装了一个对象,这样可以保证不会出现空指针。

位操作符

shl(bits)  左移位 (Javas <<) left
shr(bits)  右移位 (Javas >>)  right
ushr(bits)  无符号右移位 (Javas >>>) unsigal
and(bits)  
or(bits)  
xor(bits)  异或
inv()  反向

数组

用类 Array 实现,并且还有一个 size 属性及 get 和 set 方法,

两种实现

  • 一种是使用函数arrayOf();
  • 另外一种是使用工厂函数。
    //[1,2,3]
    val a = arrayOf(1, 2, 3)
    //[0,2,4]
    val b = Array(3, { i -> (i * 2) })
    

    类Array,还有ByteArray, ShortArray, IntArray,用来表示各个类型的数组,省去了装箱操作,因此效率更高,其用法同Array一样:

vararg 可变长参数

fun vars(vararg  i:Int){
    i.forEach {
      print("$it  ")
    }
}
fun main() {
  vars(1,2,3,4) // 1  2  3  4  
}
结果就是

to

val map = mapOf(
    "x" to 1, "y" to 2
)
println(map)

to 前后 组成一个 Pair ,可以理解为一个键值对

When 表达式

when 既可以被当做表达式使用也可以被当做语句使用。 如果它被当做表达式,符合条件的分支的值就是整个表达式的值. 如果当做语句使用, 则忽略个别分支的值。

when(i){
     1-> print(i)
     0-> print(" value is "+i)
     2,3-> print("合并")
     in 4..6-> print("检验是否在这个区间")
     !in 8..9 -> print("检验不在这个区间")
     else ->{
         print(" else 中是代码块")
     }
 }

when 中使用 in 运算符来判断集合内是否包含某实例:

val items = setOf("apple", "banana", "kiwi")
when {
    "orange" in items -> println("juicy")
    "apple" in items -> println("apple is fine too")
}

reified

具体化。作为Kotlin的一个方法泛型关键字,它代表你可以在方法体内访问泛型指定的JVM类对象。

inline fun <reified T: Any> Gson.fromJsonNew(json: String): T{
//封装了`Gson.fromJson(String json , Class<T> classOf)`方法
    return fromJson(json, T::class.java)
}

这里需要指定T类型为Any,即Object类。接着可以不需要传入Class,直接调用

fun main(args: Array<String>) {
    val json = "{state:0,result:'success',name:'test'}"
    var result : ReifiedBean =  Gson().fromJsonNew(json)
    println(result.name+","+result.result)
}

这要归功于inline,inline 意味着编译的时候真正要编译到调用点。那么哪个方法调用了它,参数的类型都是确定的。也就不需要传入Class了

类和对象

Koltin 中的类可以有一个 主构造器(使用constructor修饰),以及一个或多个次构造器(加前缀 constructor 修饰), 主构造器是类头部的一部分,位于类名称之后: 次构造器是位于类主体内,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数

主构造器不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀

class Person constructor(firstName: String) {
  //在同一个类中代理另一个构造函数使用 this 关键字:
    constructor (name: String, age:Int) : this(name) {
        // 初始化...
    }
    init {
        println("FirstName is $firstName")
    }

}

嵌套类 和内部类

内部类使用 inner 关键字来表示。 内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。

class Outer{                  // 外部类
    private val bar: Int = 1
    class Nested {             // 嵌套类
        fun foo() = 2
    }
    var v = "成员属性"
    inner class Inner {    //内部类
        fun foo() = bar  // 访问外部类成员
        fun innerTest() {
            var o = this@Outer //获取外部类的成员变量
            println("内部类可以引用外部类的成员,例如:" + o.v)
        }
    }
}

使用如下

fun main(args: Array<String>) {
    val demo = Outer.Nested().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性
    println(demo)    // == 2

    val demo = Outer().Inner().foo() //调用格式
    println(demo) //   1
    val demo2 = Outer().Inner().innerTest()   
    println(demo2)   // 内部类可以引用外部类的成员,例如:成员属性
}

注意调用格式

Kotlin 继承

Kotlin 中所有类都继承该 Any 类,它是所有类的超类,对于没有超类型声明的类是默认超类:

  • open 使用open的类/ 方法表示可以继承/重写

    使用 : 符号来表示继承

  • 属性扩展/函数扩展 可以在不修改源码的情况下,给一个类新增一个方法或者属性

open class A {
    open fun f () { print("A") }
    fun a() { print("a") }
}

interface B {
    fun f() { print("B") } //接口的成员变量默认是 open 的
    fun b() { print("b") }
}

class C() : A() , B{  //继承类和实现接口的区别,
    override fun f() {
        super<A>.f()//调用 A.f()
        super<B>.f()//调用 B.f()
    }
}

fun main(args: Array<String>) {
    val c =  C()
    c.f();

}

Kotlin 扩展

Kotlin 数据类与密封类

数据类

关键字 data

data class User(val name: String, val age: Int)

Executors.newSingleThreadExecutor { r -> Thread(r, "hello") }
等价于
Executors.newSingleThreadExecutor(object : ThreadFactory {
    override fun newThread(r: Runnable): Thread {
        return Thread(r, "hello")
    }
})

newSingleThreadExecutor()接收一个ThreadFactory 类型的参数,而ThreadFactory 刚好又是一个interface,所以创建interface对象,就需要用到object 关键字


搬运地址:

Kotlin Jetpack 实战|00. 写给 Java 开发者的 Kotlin 入坑指南

写给Android开发者的Kotlin入门

Kotlin 教程

学习Android Jetpack? 实战和教程这里全都有!

LiveData && ViewModel使用详解

[译]掌握Kotlin中的标准库函数: run、with、let、also和apply