扫盲系列 - kotlin 语法
- 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来表示所有类的基类
::
- 一个方法当做一个参数,传递到另一个方法中进行使用,通俗来讲就是引用一个方法
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) – 左移位 (Java’s <<) left
shr(bits) – 右移位 (Java’s >>) right
ushr(bits) – 无符号右移位 (Java’s >>>) 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 入坑指南
既已览卷至此,何不品评一二: