Kotlin
线上编写练习
Kotlin官网提供了 PlayGround 供大家线上尝试
地址:https://play.kotlinlang.org/
原文:https://www.jianshu.com/p/ae5f15ceb9b0
本来没打算把关键字
介绍放在第一部分,可是发现,如果不放在这儿,后面很多用到关键字的地方会有很多不明白不了解的情况。
所以还是把关键字的介绍放在了第一部分,不需要全部记住,但是在后面看到的时候,可以随时翻到这篇文章,可以做一个简单的了解。
本文简单的介绍了Kotlin
中使用的部分常用关键字。
希望对大家初步了解Kotlin
有所帮助。
Kotlin 官网文档
Keywords
https://kotlinlang.org/docs/keyword-reference.html
Kotlin 的关键字
大致可以分为 三类:
关键字
,不可以作为任何标识符。关键字
,在其不起作用的上下文中可以作为标识符使用。关键字
,也可以在其它地方用作标识符Kotlin 官网文档
Hard Keywords
https://kotlinlang.org/docs/keyword-reference.html#hard-keywords
下面的这些符号任何情况下都被优先解析为关键字
,不可以作为任何标识符。
as
// 数据类型转换
val obj: Any = 1
// 可能会抛出 java.lang.ClassCastException,可以考虑 is 或者 as? 替代
val intValue: Int = obj as Int
// 处理Nullable
val intValueNullable: Int? = obj as? Int
// is 之后可以直接使用对应属性
if (obj is Int) {
print("${obj.plus(1)}")
}
==================================================
// import 别名设置
import com.unluck.ktdemo.DemoClass
import com.unluck.ktdemo.bean.DemoClass as DemoBean // 同时引入DemoClass会冲突,可以考虑通过别名引入
as?
用于 安全的类型转换.// 空安全,但是需要处理 Nullable
val obj: Any? = null
val intValueNullable: Int? = obj as? Int
break
终止一个循环操作.// break 终止 for
for (i in 1..100) {
if (...) break
}
// break to labels 可以声明labels 终止指定的for循环
breakToThis@ for (i in 1..100) {
for (j in 1..100) {
if (...) break@breakToThis // 终止 breakToThis@ for(i)
}
}
class
声明一个类.// 声明类 DemoClass
class DemoClass {}
// 声明类 MainActivity 并继承自 AppCompatActivity
class MainActivity : AppCompatActivity()
// 声明类 DemoTextView 并继承自 AppCompatTextView
class DemoTextView(ctx : Context) : AppCompatTextView(ctx)
// 声明数据类 DemoBean 并实现 Serializable 接口
data class DemoBean(val name: String) : Serializable
continue
继续当前循环的下一个循环.// 同break使用方式类似
for (i in 1..100) {
if (...) continue
}
// continue to labels 可以声明labels 继续指定的for循环的下一个循环
continueToThis@ for (i in 1..100) {
for (j in 1..100) {
if (...) continue@continueToThis
}
}
do
do/while 循环 (a loop with a postcondition) 的开始标识.do {
...
} while (true)
else
定义一个 if 表达式 的 false 时的执行分支.if (...)
...
else
...
false
Boolean 值的 false 值.val bool : Boolean = false
for
开始一个 for 循环.for (i in 1..100) {
print("print i = $i")
}
fun
声明一个 函数.fun mainFunction() {
...
}
if
if 表达式 的起始.if (i == 0) ...
in
// for 迭代
for (i in 6 downTo 0 step 2) {
println(i)
}
// check in range
if (3 in 0..4)
// check contains
val list = arrayListOf<Any>()
if ( "1" in list ) {
...
}
// when branch
val num = 3
when {
num in 0..4 -> print("the num $num is in range 0-4")
else -> {}
}
// 逆变参数
interface Comparable<in T> {
operator fun compareTo(other: T): Int
}
!in
// check not in range
if (6 !in 0..4)
// check contains
val list = arrayListOf<Any>()
if ( "1" !in list ) {
...
}
// when branch
val num = 7
when {
num !in 0..4 -> print("the num $num is not in range 0-4")
else -> {}
}
interface
声明一个 interface 接口.interface Comparable<in T> {
operator fun compareTo(other: T): Int
}
is
// check value typecasts
if ( obj is Int)
...
// when branch
when (value) {
is GroupMsg -> print("this is group message")
else -> print("other message")
}
!is
is
功能相对null
就是null
啊,表示对象没有任何对象引用.
val nullableValue : Any? = null
object
用于同时声明了 一个类和它的实例.
// 全局单例
object SingletonClass {
var mInstanceValue = 999
fun singletonFunString() = "Singleton Class"
}
// 伴生对象
class DemoUtil {
companion object {
@JvmStatic
fun companionObjectFun() = "this is companion object function"
}
}
// 对象表达式 -> 某个Any对象
val helloWorld = object {
val hello = "Hello"
val world = "World"
// object expressions extend Any, so `override` is required on `toString()`
override fun toString() = "$hello $world"
}
// 对象表达式 -> 实现某个接口
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { /*...*/ }
override fun mouseEntered(e: MouseEvent) { /*...*/ }
})
// 对象表达式 -> 实现抽象类
open class A(x: Int) {
public open val y: Int = x
}
interface B { /*...*/ }
val ab: A = object : A(1), B {
override val y = 15
}
package
指定 当前文件的 package , 与实际文件路径无关.// package 仅代表当前文件所属的 package 属性
// 与 java 相比,其package无需与文件的实际路径相关,但是建议保持一致。
package com.unluck.ktdemo.bean // file://com/unluck/ktdemo/data
return
从当前函数返回.// 同 java 类似,从当前函数返回
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return // non-local return directly to the caller of foo()
print(it)
}
println("this point is unreachable")
} // result -> 12
// 在 forEach 中使用 labels 则类似 continue , 其实 return 的是执行的 lamda
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit // local return to the caller of the lambda - the forEach loop
print(it)
}
print(" done with explicit label")
} // result -> 1245 done with explicit label
super
// 调用超类方法
class Circle() : Shape() {
override fun draw() {
super.draw()
println("Circle")
}
}
// 调用超类构造
class DemoView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
this
throw
抛出一个异常.
throw NoBugException()
true
Boolean type value true.val bool : Boolean = true
try {
...
} catch ( e : Exception) {}
typealias
声明一个 类型别名.class A {
inner class Inner
}
class B {
inner class Inner
}
typealias AInner = A.Inner
typealias BInner = B.Inner
val finalValue : Int = 1
var variable : Int = 1
when
when 表达式 .when {
a == b -> {}
else -> {}
}
when(a) {
0 -> {}
else -> {}
}
while
while 循环表达式 .while (true) {
...
}
Kotlin 官网文档
Soft Keywords
https://kotlinlang.org/docs/keyword-reference.html#soft-keywords
下面的这些符号在其适用的上下文中充当关键字,并且可以在其他上下文中用作标识符。
interface Printer {
fun print()
}
class DefaultPrinter : Printer {
override fun print() {
print("default printer")
}
}
class DemoFragment : Fragment(), Printer by DefaultPrinter() { // 委托实现接口
val vm : ViewModel by viewModels() // 委托属性
fun doSomePrint() {
print() // print -> default printer
}
}
catch
处理捕获的异常信息.try {
...
throw NoBugException("Yes , NO BUG !!! ")
...
} catch (e : NoBugException) {
print("NO BUG ~ ")
} catch (e2 : Exception) {
print("F..k !!!! ")
}
constructor
声明构造函数.class DemoView : View {
constructor(ctx: Context) : super(ctx) {
...
}
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
delegate
用作注解相关.
dynamic
引用一个 Kotlin/JS 代码中的 dynamic type (动态类型).
在面向 JVM 平台的代码中不支持动态类型
field
用作 注解相关.
file
用作 注解相关.
finally
标识 当 try 块退出时总会执行的代码块.(不保证立刻执行)
try {
...
} finally {
// optional finally block
}
get
val isEmpty: Boolean
get() = this.size == 0
import
将另一个包中的声明导入当前文件.import com.unluck.ktdemo.Demo
init
声明 初始化代码块.class Demo {
init {
print("init block")
}
}
param
用作 注解相关.
property
用作 注解相关.
receiver
用作 注解相关.
set
var setterVisibility: String = "abc"
private set // the setter is private and has the default implementation
var setterWithAnnotation: Any? = null
@Inject set // annotate the setter with Inject
// To declare an inline class, use the value modifier before the name of the class:
value class Password(private val s: String)
where
指定泛型类型参数的约束.fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
where T : CharSequence,
T : Comparable<T> {
return list.filter { it > threshold }.map { it.toString() }
}
Kotlin 官网文档
Modifier keywords
https://kotlinlang.org/docs/keyword-reference.html#modifier-keywords
以下符号作为声明中修饰符列表中的关键字,并可用作其他上下文中的标识符。
@SinceKotlin("1.1") public actual typealias RandomAccess = java.util.RandomAccess
@SinceKotlin("1.1") public actual typealias ArrayList<E> = java.util.ArrayList<E>
@SinceKotlin("1.1") public actual typealias LinkedHashMap<K, V> = java.util.LinkedHashMap<K, V>
@SinceKotlin("1.1") public actual typealias HashMap<K, V> = java.util.HashMap<K, V>
@SinceKotlin("1.1") public actual typealias LinkedHashSet<E> = java.util.LinkedHashSet<E>
@SinceKotlin("1.1") public actual typealias HashSet<E> = java.util.HashSet<E>
annotation
声明一个 注解类.@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class Fancy
companion
声明一个 伴生对象.class DemoClass {
companion object Factory {
fun create(): DemoClass = DemoClass()
}
}
const
标记属性为 编译时常量.const val STATIC_FINAL_STRING: String = "public static final String"
crossinline
指示内联函数的 lambda 参数 禁止 使用非局部返回.public inline fun <T, R : Comparable<R>> Iterable<T>.sortedBy(crossinline selector: (T) -> R?): List<T> {
return sortedWith(compareBy(selector))
}
data
声明一个 数据类.// 编译器会自动为其主构造函数中的属性生成 equals ,hashCode,toString,copy等方法
data class DataBean (
val name : String = "",
val age : Int = 0,
var nick : String = "",
) : Serializable
enum
声明一个 枚举类.enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
expect
将一个声明标记为平台相关, 并期待在平台模块中实现.
external
将一个声明标记为不是在 Kotlin 中实现(通过 JNI访问或者在JavaScript中实现).
final
禁止 重写方法.
open class Rectangle() : Shape() {
final override fun draw() { /*...*/ }
}
infix
允许以中缀表示法调用函数.@file:kotlin.jvm.JvmName("TuplesKt")
package kotlin
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
// use to
val pair : Pair<String,String> = "A" to "B"
inline
内联标识,告知编译器 在调用处 内联 传给它的 函数 或 lambda 表达式.@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("MapsKt")
@file:OptIn(kotlin.experimental.ExperimentalTypeInference::class)
package kotlin.collections
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun hashMapOf(): HashMap = HashMap()
inner
标识 内部类 会持有外部引用.// kotlin 默认非声明 inner 的 class 为静态内部类
// 因此如需持有外部引用,需要单独添加 inner 标识
class Outer {
private val outValue: Int = 1
inner class Inner {
fun print() = outValue
}
}
val print = Outer().Inner().print() // print == 1
internal
标识 可见性仅当前 module 可用.package kotlin.collections
expect interface RandomAccess
...
internal expect fun copyToArrayImpl(collection: Collection<*>): Array<Any?>
internal expect fun <T> copyToArrayImpl(collection: Collection<*>, array: Array<T>): Array<T>
internal expect fun <T> arrayOfNulls(reference: Array<T>, size: Int): Array<T>
internal expect fun <K, V> Map<K, V>.toSingletonMapOrSelf(): Map<K, V>
internal expect fun <K, V> Map<out K, V>.toSingletonMap(): Map<K, V>
internal expect fun <T> Array<out T>.copyToArrayOfAny(isVarargs: Boolean): Array<out Any?>
lateinit
标识一个非空属性可以 延迟初始化.class DemoClass {
lateinit var lateValue : DemoBean
fun doLateInit() {
if (!this::lateValue.isInitialized) {
lateValue = DemoBean()
}
}
}
noinline
传给内联函数的 lamda 表达式 不使用 内联.
open
允许 继承和重写.open class Person {
open fun age() = 10
}
class OldMan : Person() {
override fun age() = 100
}
operator
将方法声明为 重载运算符 或 约定运算.
Kotlin 约定了一些函数规则,与之对应的有一系列运算符或者其它关键字约定。
详细可参考官网对应文档,这里不再一一指出。
out
标记参数类型为 协变.
表示告诉编译器,你只操作数据,并不变更消费数据,不关心其类型。
public data class Pair<out A, out B>(
public val first: A,
public val second: B
) : Serializable
override
标识一个方法为 重写方法.open class Person {
open fun age() = 10
}
class OldMan : Person() {
override fun age() = 100
}
private
标识可见性 其可见性仅当前 class 或 file 可用.
protected
标识可见性 其可见性仅当前 class 或 其子类 可用.
public
标识可见性 全部可见.
reified
将内联函数的类型参数标记为 在运行时可访问.
@MainThread
inline fun <reified VM : ViewModel> Fragment.viewModels(
noinline ownerProducer: () -> ViewModelStoreOwner = { this },
noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)
public suspend fun delay(timeMillis: Long) {
if (timeMillis <= 0) return // don't delay
return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
}
}
tailrec
将一个函数标记为 尾递归(允许编译器将递归替换为迭代).
vararg
允许一个参数传入可变数量的参数.
// mutableListOf
public fun <T> mutableListOf(vararg elements: T): MutableList<T> =
if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
// vararg
val list = mutableListOf(1,2,3,4)
Kotlin 官网文档
Modifier keywords
https://kotlinlang.org/docs/keyword-reference.html#special-identifiers
以下标识符由编译器在指定上下文中定义,并且可以用作其他上下文中的常规标识符。
field
用在属性访问器内部来引用该 属性.var counter = 0 // the initializer assigns the backing field directly
set(value) {
if (value >= 0)
field = value
// 不可以使用 counter = value ,否则会导致递归调用set ,ERROR StackOverflow
// ERROR StackOverflow: Using actual name 'counter' would make setter recursive
}
it
用在 lambda 表达式内部表示 隐式引用其参数.// 隐式引用
(1..100).random().takeIf {
it > 50 // it -> random 得到的 value
}?.let {
// it -> takeIf 函数的 返回值 ,
print("random num $it")
}
// 逻辑较多时 或 有嵌套 lamda,还是建议显示的声明其参数
(1..100).random().takeIf { randomNum ->
randomNum > 50
}?.let { num ->
print("random num $num ")
}
Kotlin 以下操作符及部分特殊符号的定义
+
, -
, *
, /
, %
常见的数学操作符
*
展开运算符 将一个 Array 数据展开成 varargs.// mathematical operators
val plus = 1 + 2
val minus = 2 - 1
val times = 2 * 2
val div = 2 / 2
val rem = 1 % 2
// 展开运算符
fun foo(arr : Array<String>) {
val list = listOf("vararg params", *arr) // 展开运算符 展开数组内容
}
=
// 赋值
val key : String = "key"
// 默认参数
fun hash(key : String = "") {}
+=
, -=
, *=
, /=
, %=
运算赋值操作符.var num = 1
num += 1 // num = 1 + 1 = 2
num -= 1 // num = 2 - 1 = 1
num *= 8 // num = 1 * 8 = 8
num /= 2 // num = 8 / 2 = 4
num %= 3 // num = 4 % 3 = 1
++
, --
递增和递减操作符.a++
++a
a--
--a
&&
, ||
, !
逻辑“与”、“或”、“非”操作符(对于位运算,请使用相应的中缀函数) .if (nameStr.isNullOrEmpty() || psd.isNullOrBlank()) {
...
}
==
, !=
相等和不等运算符 (非基本类型会直接调用 equals()
).val a = DemoBean()
val b = DemoBean()
a == b // -> a?.equals(b) ?: (b === null)
a != b // -> !(a?.equals(b) ?: (b === null))
// Float or Double
// 静态化浮点数
print(-0.0f == 0.0f) // true
print(Float.NaN == Float.NaN) // false
print(Float.NaN > Float.POSITIVE_INFINITY) // false
// 特殊情况。 当通过非基本类型比较时,会出现以下结果
// 非静态化为浮点数 例如 :Any
val numZero1 : Any = -0.0f
val numZero2 : Any = 0.0f
println(numZero1 == numZero2) // false 通过 equals 比较
// NaN
val firstNaN : Any = Float.NaN
val secondNaN = Float.NaN
println(firstNaN == secondNaN) // true 通过 equals 比较
===
, !==
引用比较等值操作符 比较的是内存引用.a === b
a !== b
<
, >
, <=
, >=
比较运算符 (非基本类型会调用 compareTo()
).a > b // a.compareTo(b) > 0
a < b // a.compareTo(b) < 0
a >= b // a.compareTo(b) >= 0
a <= b // a.compareTo(b) <= 0
// Float 和 Double 存在特殊情况
// 静态化浮点数
print(-0.0f == 0.0f) // true
print(Float.NaN == Float.NaN) // false
print(Float.NaN > Float.POSITIVE_INFINITY) // false
// Comparable
val numZero3 : Comparable<Float> = -0.0f
val numZero4 : Float = 0.0f
println(numZero1 < numZero2) // true 通过 compareTo 比较
// 无穷
val compareNaN : Comparable<Float> = Float.NaN
val positiveInfinity = Float.POSITIVE_INFINITY // 正无穷
println(compareNaN > positiveInfinity) // true 通过 compareTo 比较
[
, ]
- 索引访问操作符 (会调用对应的 get
和 set
).val item = array[i]
array[i] = value
val value = hashMap[key]
hashMap[key] = value
!!
非空断言表达式 如果为空则直接抛 NullPointerException.fun getNum() : Int? {
return (1..10).random().takeIf { it > 5} // 返回大于 5 的 Int,否则 返回 null
}
val nonNullValue : Int = getNum()!! // 如果 getNum() 返回 null ,则直接抛 NullPointerException
?.
执行 空安全调用 (如果调用者非空才调用方法或属性).fun getNum() : Int? {
return (1..10).random().takeIf { it > 5} // 返回大于 5 的 Int,否则 返回 null
}
val num : Int? = getNum()
num?.plus(1)
?:
如果左侧的值为空,就取右侧的值 ( elvis 操作符).fun getNum() : Int? {
return (1..10).random().takeIf { it > 5} // 返回大于 5 的 Int,否则 返回 null
}
val num : Int = getNum() ?: 1 // getNum 为空时,num 等于 右侧的 1
// member
fun isOdd(x: Int) = x % 2 != 0
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd)) // ::isOdd ==> 代表isOdd方法,类型为 block(x) -> Boolean
// class
Intent(this@MainActivity,SecondActivity::class.java)
..
创建一个 区间.1..3 // 1,2,3
1..100 // 1,2,3 ... 98,99,100
:
分隔声明中的名称与类型.val name : String = ""
?
将类型标记为 可空类型.val name : String? = null
->
分隔 lambda 表达式 的参数和方法体.
分隔 函数类型 中的参数类型和返回类型.
分隔 when 表达式 的分支条件和执行方法.
// lamda
view.setOnClickListener { view ->
print("view click")
}
v.setOnTouchListener { view, event ->
print("view touch event ${event.action}")
false
}
// 函数类型
fun check(block : (Boolean) -> Unit) {
block(true)
block.invoke(false)
}
check {
print(it)
}
// when
when(value) {
0 -> {}
else -> {}
}
@
引用 注解.
引入或者使用 循环的 label.
引用或者使用 lambda label.
引用一个 外部作用域的 ‘this’ 表达式.
引用 外部 superclass.
// 注解
@Route(path = "/user/center")
class UserCenterActivity
// loop label
breakToThis@ for (i in 1..100) {
for (j in 1..100) {
if (...) break@breakToThis // 终止 breakToThis@ for(i)
}
}
// lamda label
fun foo() {
run loop@{
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@loop // non-local return from the lambda passed to run
print(it)
}
}
print(" done with nested loop")
}
// this
class MainActivity .. {
inner class Factory {
fun create() {
Intent(this@MainActivity,SecondActivity::class.java)
}
}
}
// super class
class MainActivity : FragmentActivity... {
inner class Factory {
fun create() {
val intent = Intent(this@MainActivity,SecondActivity::class.java)
super@MainActivity.startActivity(intent)
}
}
}
;
分隔位于同一行的多个语句.val v = View(context); val t = Timer()
$
在 字符串模板 中引用变量或者表达式.print("num $num ")
print("other ${other.num} ")
_
未使用的参数,可以用该符号替代
lambda 表达式 中未使用的参数,可以用该符号替代.
解构声明 未使用的参数.
// lamda
v.setOnTouchListener { _, _ ->
// fix all event
true
}
// 解构
val (_, status) = getResult()
如果要查看各个运算符的优先级,可以查看 这篇官方文档 .
Kotlin 官网文档
Keywords
https://kotlinlang.org/docs/keyword-reference.html