学习站点
文章
知识点
类的构造函数及init代码块调用顺序
- init代码块 -> 构造函数
- 示例
class L3C1 {
var attr1:String? = null
constructor(a1:String){
Log.i("L3C1","constructor. start.")
attr1 = a1
Log.i("L3C1","constructor. end.")
}
init {
Log.i("L3C1","init")
}
}
fun showConstructorInit(view: View) {
val bean:L3C1 = L3C1("testA1")
}
日志打印:
2023-10-31 13:55:00.763 4588-4588/com.example.jet2022 I/L3C1: init
2023-10-31 13:55:00.763 4588-4588/com.example.jet2022 I/L3C1: constructor. start.
2023-10-31 13:55:00.763 4588-4588/com.example.jet2022 I/L3C1: constructor. end.
val使用get可以返回动态值
class L3C2 {
val a1: String
get() {
val result = "" + Math.random()
Log.i("L3C2", "a1. get. $result")
return result
}
}
fun showValDynamicValue(view: View) {
val bean:L3C2 = L3C2()
var a = bean.a1
a = bean.a1
a = bean.a1
}
2023-10-31 16:36:01.610 21094-21094/com.example.jet2022 I/L3C2: a1. get. 0.10962064620199585
2023-10-31 16:36:01.610 21094-21094/com.example.jet2022 I/L3C2: a1. get. 0.06812714631783268
2023-10-31 16:36:01.610 21094-21094/com.example.jet2022 I/L3C2: a1. get. 0.33978948467382875
object实现单例
object L3O1 {
val a1: String
get() {
Log.i("L3O1","a1. get.")
return Math.random().toString() + "1111111"
}
fun f1(){
Log.i("L3O1","f1")
}
}
fun showObjectSingleInstance(view: View) {
L3O1.a1
L3O1.f1()
}
2023-10-31 16:53:24.389 23115-23115/com.example.jet2022 I/L3O1: a1. get.
2023-10-31 16:53:24.390 23115-23115/com.example.jet2022 I/L3O1: f1
主构造函数
- 和class一起声明的constructor是主构造函数
- 主构造函数中的值可以给类中的属性引用,也可以在init中引用
- 主构造函数中使用var或val声明属性
- 该类中包含同名可变属性及不可变属性
- 该类中上述属性值就是主构造函数传入值
- class内部的constructor属于次级构造参数
- 次级构造函数声明时,必须继承主构造函数
- 示例
//和类一起声明的构造函数是主构造函数
//主构造函数中的值可以给类中的属性引用,也可以在init中引用
class L4C1 constructor(a1: String, a2: String) {
var a1: String = a1
var a2: String = a2
var a3: String = ""
init {
println(a1)
println(a2)
}
//次级构造函数声明时,必须继承主构造函数
constructor(a3: String) : this("a1", "a2") {
this.a3 = a3
}
}
//主构造函数中使用var或val声明属性
//1:该类中包含同名可变属性及不可变属性
//2:该类中上述属性值就是主构造函数传入值
class L4C2 constructor(var a1: String, val a2: String) {
}
Kotlin泛型
out 和 in
- out
- 默认写法,无法将一个子类型List赋值给1个声明的父类型List
- 声明中使用out,可以解决该问题
- 使用out后,get支持,add,set不支持
var list1:MutableList<out TextView> = arrayListOf<Button>( Button(this,null,0), Button(this,null,0), Button(this,null,0) ) - in
- 默认写法,无法将一个父类型List赋值给1个声明的子类型List
- 声明中使用in,可以解决该问题
- 使用in后,get不支持,add,set支持
- get后强转是可以的
var list2:MutableList<in TextView> = arrayListOf<View>( Button(this,null,0), Button(this,null,0), Button(this,null,0) )
可以在类声明的时候,给泛型符T增加out和in关键字
//可以在声明类时,给泛型符T添加out关键字
class COUT<out T> {
}
//可以在声明类时,给泛型符T添加in关键字
class CIN<in T> {
fun f1(t:T){
}
}
fun f2(){
//可以在类声明的时候,给泛型符T加out或in关键字
val cout:COUT<View> = COUT<ViewGroup>()
val cin:CIN<ViewGroup> = CIN<View>()
cin.f1(LinearLayout(this,null,0))
}
where支持为泛型类设置多个上界
interface PI1 {
fun pi1func1()
}
interface PI2 {
fun pi2func1()
}
class CPI1PI2 : PI1,PI2 {
override fun pi1func1() {
}
override fun pi2func1() {
}
}
//where可以为类泛型符T设置多个上界
class CC1<T> where T : PI1, T : PI2 {
var instance:T? = null
fun addInstance(t:T){
instance = t
}
fun exeFunc(){
instance?.pi1func1()
instance?.pi2func1()
}
}
fun f4(){
val item:CC1<CPI1PI2> = CC1<CPI1PI2>()
item.addInstance(CPI1PI2())
item.exeFunc()
}
inline和reified结合,可以在运行时判断泛型实例的具体类型
/*fun <T> f3(p1:Any){
if (p1 is T){
println("111")
}
}*/
//inline和reified结合,可以在运行时判断泛型实例确切类型
inline fun <reified T> f3(p1:Any){
if (p1 is T){
println("111")
}
}
当你没有对泛型参数应用任何边界时,它默认为 Any?
'lateinit' modifier is not allowed on properties of a type with nullable upper bound
class C2<T1, T2> {
//因为泛型未添加限制,其类型是Any?,对于可空类型变量,无法使用lateinit
lateinit var t1: T1
lateinit var t2: T2
}
要这样改:如果不限制泛型类型范围,让其继承ny
class C2<T1 : Any, T2 : Any> {
lateinit var t1: T1
lateinit var t2: T2
fun setInstance(t1: T1, t2: T2) {
this.t1 = t1
this.t2 = t2
}
fun print() {
Log.i("TagC2", "print. t1:$t1 ; t2:$t2")
}
}
fun testTClass(view: View) {
val item: C2<String, Int> = C2()
item.setInstance("111", 222)
item.print()
}
Kotlin协程简单使用
- Android项目引入协程
E:\Data\Code\Jet\Jet2022\app\build.gradle
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.1"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.1'
***
}
- 代码中构建协程,在协程中执行多线程协作
fun doFunc1(view: View) {
GlobalScope.launch(Dispatchers.Main) {
//闭包里面的代码块称为1个协程
var a1: Int = withContext(Dispatchers.IO) {
Log.i(TAG, "gin a1")
100
}
var a2: Int = withContext(Dispatchers.Default) {
Log.i(TAG, "gain a2")
200
}
Log.i(TAG, "result:${a1 + a2}")
}
val cs: CoroutineScope = CoroutineScope(Dispatchers.IO)
cs.launch {
//闭包里面的代码块称为1个协程
Log.i(TAG, "111")
}
Log.i(TAG, "isActive:${cs.isActive}")
}
2023-11-07 17:46:10.956 2303-2303/com.example.jet2022 I/TagKotlinLesson6Activity: isActive:true
2023-11-07 17:46:10.958 2303-2407/com.example.jet2022 I/TagKotlinLesson6Activity: 111
2023-11-07 17:46:10.963 2303-2407/com.example.jet2022 I/TagKotlinLesson6Activity: gin a1
2023-11-07 17:46:10.968 2303-2425/com.example.jet2022 I/TagKotlinLesson6Activity: gain a2
2023-11-07 17:46:10.968 2303-2303/com.example.jet2022 I/TagKotlinLesson6Activity: result:300
协程中调度器/Dispatchers有三种,适用场景
- Dispatchers.Main
- Android主线程
- Dispatchers.IO
- 读写文件,操作数据库,网络请求
- Dispatchers.Default
- CPU密集型任务,比如大量的计算
delay可以实现等待一段时间的效果
suspend fun f1(): String {
Log.i("KotlinLesson7Activity", "1111111")
delay(2000)
Log.i("KotlinLesson7Activity", "2222222")
return withContext(Dispatchers.Default) {
Log.i("KotlinLesson7Activity", "3333333")
"333333333"
}
}
fun testSuspend(view: View) {
Log.i("KotlinLesson7Activity", "testSuspend. start.")
GlobalScope.launch(Dispatchers.IO) {
f1()
}
Log.i("KotlinLesson7Activity", "testSuspend. end.")
}
2023-11-14 14:57:48.586 9639-9639/com.example.jet2022 I/KotlinLesson7Activity: testSuspend. start.
2023-11-14 14:57:48.635 9639-9639/com.example.jet2022 I/KotlinLesson7Activity: testSuspend. end.
2023-11-14 14:57:48.638 9639-9706/com.example.jet2022 I/KotlinLesson7Activity: 1111111
2023-11-14 14:57:50.646 9639-9706/com.example.jet2022 I/KotlinLesson7Activity: 2222222
2023-11-14 14:57:50.651 9639-9708/com.example.jet2022 I/KotlinLesson7Activity: 3333333
扩展函数
扩展函数包括无参数的扩展函数,有参数的扩展函数
class L10C1 {
//类中的扩展函数,只能在类里面被调用
fun String.f1(){
Log.i("L10C1","String.f1")
}
fun f2(){
"".f1()
}
}
fun f3(){
"".f2()
}
//类外面的扩展函数,可以被其他类调用
//无参数
fun String.f2(){
}
//有参数的类的扩展函数
fun String.f3(p1:Int, p2:Float){
Log.i("L10C1","String.f3")
}
扩展函数的调用方法:
fun f1() {
//无参数的扩展函数调用方式
"".f2()
String::f2.invoke("111111111111")
(String::f2)("111111111")
//有参数的扩展函数调用方式
"".f3(1, 1F)
String::f3.invoke("StringSource", 1, 2F)
(String::f3)("StringSource", 1, 2F)
}
可以将扩展函数的引用赋值给变量
fun f2() {
//将扩展函数的引用赋值给变量
//无参数的扩展函数
val sf2: String.() -> Unit = String::f2
sf2.invoke("1111111")
sf2("1111")
"11111".sf2()
//有参数的扩展函数
val sf3: String.(Int, Float) -> Unit = String::f3
sf3.invoke("22222", 1, 2F)
sf3("222222", 1, 2F)
"222222".sf3(1, 2F)
}
扩展函数的引用有2种写法,且2种写法声明的变量可以互相赋值
fun f3() {
//扩展函数的引用,有2种写法
//1:无参数的扩展函数
val func1: String.() -> Unit = String::f2
val func2: (String) -> Unit = String::f2
func1.invoke("1111111")
func1("1111111")
"11111111".func1()
func2.invoke("1111111")
func2("1111111")
//这样调用是不行的
//"11111111".func2()
//两种类型的写法声明的变量可以互相赋值
val func3: String.() -> Unit = func2
val func4: (String) -> Unit = func1
//2: 有参数的扩展函数
val func5: String.(Int, Float) -> Unit = String::f3
val func6: (String, Int, Float) -> Unit = String::f3
func5.invoke("111111", 1, 2F)
func5("11111", 1, 2F)
"111111111".func5(1, 2F)
func6.invoke("111111", 1, 2F)
func6("11111", 1, 2F)
//这样调用是不行的
//"1111111".func6(1,2F)
val func7: String.(Int, Float) -> Unit = func6
val func8: (String, Int, Float) -> Unit = func5
}
因为上述两种写法声明的变量可以互相赋值,进而:
- 可以将一个扩展函数的引用赋值给一个非扩展函数类型声明的变量
- 可以禁止该变量的 Instance.variableName(params...) 调用方式
- 可以将一个非扩展函数的引用赋值给一个扩展函数类型声明的变量
- 可以增加该变量的 Instance.variableName(params...) 调用方式
示例:
fun Int.f5(p1: String, p2: String) {
}
fun f6(p1: Int, p2: String, p3: String) {
}
fun f4() {
//因为上述两种写法声明的变量可以互相赋值,进而:
//1: 可以将一个扩展函数的引用赋值给一个非扩展函数类型声明的变量
//1.1: 可以禁止该变量的 Instance.variableName(params...) 调用方式
val f1: (Int, String, String) -> Unit = Int::f5
//这样会报错
//100.f1("111","222")
//2: 可以将一个非扩展函数的引用赋值给一个扩展函数类型声明的变量
//2.1: 可以增加该变量的 Instance.variableName(params...) 调用方式
val f2: Int.(String, String) -> Unit = ::f6
//可以这样调用
100.f2("1111", "2222")
}
扩展属性
class L10c2 {
var a1: String = ""
get() {
return ""
}
set(value) {
field = value
}
private var a11:String = "a11"
}
var L10c2.a2:Int
get() {
return 10 * this.a1.toInt()
}
set(value) {
//注意:扩展属性无法真正为类增加一个属性,如果一定要存储扩展属性的值,可以利用类的非扩展属性
this.a1 = value.toString()
//扩展属性无法访问类的private声明的属性
//this.a11
}
val L10c2.a3:String
get() {
return "任意值"
}
fun f5(){
var obj:L10c2 = L10c2()
//a2, a3 都是L10c2类的扩展属性
obj.a2 = 200
var a3Value = obj.a3
}
- 类的扩展属性不能有初始化值:下面写法就是错的,Initializer is not allowed here because this property has no backing field
var L10c2.a2:Int = 100 *** - 类的扩展属性无法为类真正增加一个属性,所以set方法可以利用该类的非扩展属性
- 类的扩展属性无法访问类的private属性
inline、noinline、crossinline
高阶函数:可以以其他函数作为方法参数或返回值的函数
- 高阶函数调用时,函数参数可以用fun,也可以直接写lambda表达式
- fun中的return,仅结束当前函数类型参数的执行,不会结束外部函数整体执行
- lambda表达式中,不能直接使用return
fun f1(p1: String, func1: () -> Unit, func2: (String, String) -> Unit): Unit {
Log.i("TagInline","f1. step1")
func1()
Log.i("TagInline","f1. step2")
func2(p1, p1)
}
fun exef1() {
f1(
"111",
fun() {
//在fun中的return,仅仅结束当前函数类型参数的执行,不会结束外部函数f1整体执行流程
Log.i("TagInline","exef1. func1")
return
},
fun(s1: String, s2: String) {
Log.i("TagInline","exef1. func2")
}
)
f1(
"111",
{
Log.i("TagInline","exef1. func1")
//在lambda表达式中不可以直接使用return
//return
},
{ s1, s2 ->
Log.i("TagInline","exef1. func2")
}
)
}
2023-11-16 17:17:23.146 18931-18931 I/TagInline: f1. step1
2023-11-16 17:17:23.146 18931-18931 I/TagInline: exef1. func1
2023-11-16 17:17:23.146 18931-18931 I/TagInline: f1. step2
2023-11-16 17:17:23.146 18931-18931 I/TagInline: exef1. func2
2023-11-16 17:17:23.146 18931-18931 I/TagInline: f1. step1
2023-11-16 17:17:23.146 18931-18931 I/TagInline: exef1. func1
2023-11-16 17:17:23.146 18931-18931 I/TagInline: f1. step2
2023-11-16 17:17:23.146 18931-18931 I/TagInline: exef1. func2
inline及noinline
- 使用inline修饰的函数,在被调用时,会将其函数体,其函数类型的参数铺平,直接拷贝至调用处.
- 后果:其函数类型参数本来是一个对象,被铺平后,该对象就消失了,无法再将其函数参数作为对象使用.
- 如何解开这种限制,使用noinline修饰函数类型参数,可以继续将该参数当做对象使用
- 好处:
- 本来高阶函数中的函数类型参数,会被转换为个对象,如果该高阶函数会循环或频繁调用,就会大量创建对象引发性能问题;使用inline后直接将函数类型参数逻辑铺平拷贝过来,避免了大量创建对象损耗性能.
- 内联函数的Lambda参数中,可以使用return.
- 此处return会结束Lambda表达式的外部的外部的执行
private fun exef2Mid1(){
Log.i("TagInline","exef2Mid1. start.")
exef2Mid2()
Log.i("TagInline","exef2Mid1. end.")
}
private fun exef2Mid2(){
Log.i("TagInline","exef2Mid2. start.")
exef2Mid3()
Log.i("TagInline","exef2Mid2. end.")
}
private fun exef2Mid3(){
Log.i("TagInline","exef2Mid3. start.")
exef2()
Log.i("TagInline","exef2Mid3. end.")
}
fun exeInlineNoInlineHighFunc(view: View) {
Log.i("TagInline","exeInlineNoInlineHighFunc. start.")
exef2Mid1()
Log.i("TagInline","exeInlineNoInlineHighFunc. end.")
}
inline fun f2(p1: String, func1: () -> Unit, noinline func2: (String, String) -> Unit): (String, String) -> Unit {
Log.i("TagInline","f2. step1")
func1()
Log.i("TagInline","f2. step2")
func2(p1, p1)
//内联函数中,函数类型参数继续当做对象使用,必须使用 noinline 进行修饰
return func2
}
fun exef2() {
Log.i("TagInline","exef2. start.")
f2(
"222",
{
Log.i("TagInline","exef2. func1")
//只有内联函数的Lambda参数中,可以使用return
//在Lambda参数中的return,会结束Lambda表达式的外部的外部的执行-》结束exef2的执行
return
},
{ s1, s2 ->
Log.i("TagInline","exef2. func2")
}
)
f2(
"222",
fun() {
//在fun中的return,仅仅结束当前函数类型参数的执行
Log.i("TagInline","exef2. func1")
return
},
fun(s1: String, s2: String) {
Log.i("TagInline","exef2. func2")
}
)
Log.i("TagInline","exef2. end.")
}
2023-11-16 17:17:25.950 18931-18931 I/TagInline: exeInlineNoInlineHighFunc. start.
2023-11-16 17:17:25.950 18931-18931 I/TagInline: exef2Mid1. start.
2023-11-16 17:17:25.950 18931-18931 I/TagInline: exef2Mid2. start.
2023-11-16 17:17:25.950 18931-18931 I/TagInline: exef2Mid3. start.
2023-11-16 17:17:25.950 18931-18931 I/TagInline: exef2. start.
2023-11-16 17:17:25.950 18931-18931 I/TagInline: f2. step1
2023-11-16 17:17:25.950 18931-18931 I/TagInline: exef2. func1
2023-11-16 17:17:25.950 18931-18931 I/TagInline: exef2Mid3. end.
2023-11-16 17:17:25.950 18931-18931 I/TagInline: exef2Mid2. end.
2023-11-16 17:17:25.950 18931-18931 I/TagInline: exef2Mid1. end.
2023-11-16 17:17:25.950 18931-18931 I/TagInline: exeInlineNoInlineHighFunc. end.
inline及crossinline
- 在内联函数中间接调用函数类型参数,需要将该函数类型参数用 crossinline 修饰
- 间接调用,比如在新的线程执行该函数类型参数
- 内联函数中被crossinline修饰的函数类型参数,其lambda表达式不再享有可以使用return的权利
inline fun f3(p1: String, crossinline func1: () -> Unit, noinline func2: (String, String) -> Unit): (String, String) -> Unit {
Log.i("TagInline","f3. step1")
func1()
Log.i("TagInline","f3. step2")
func2(p1, p1)
//在内联函数中间接调用函数类型参数,需要将该函数类型参数用 crossinline 修饰
GlobalScope.launch {
func1()
}
return func2
}
fun exef3() {
f3(
"333",
fun() {
//在fun中的return,仅仅结束当前函数类型参数的执行
Log.i("TagInline","exef3. func1")
return
},
fun(s1: String, s2: String) {
Log.i("TagInline","exef3. func2")
}
)
f3(
"333",
{
Log.i("TagInline","exef3. func1")
//内联函数中被crossinline修饰的函数类型参数,其lambda表达式不再享有可以使用return的权利
//return
},
{ s1, s2 ->
Log.i("TagInline","exef3. func2")
}
)
}
2023-11-16 17:17:31.425 18931-18931 I/TagInline: f3. step1
2023-11-16 17:17:31.426 18931-18931 I/TagInline: exef3. func1
2023-11-16 17:17:31.426 18931-18931 I/TagInline: f3. step2
2023-11-16 17:17:31.426 18931-18931 I/TagInline: exef3. func2
2023-11-16 17:17:31.448 18931-18931 I/TagInline: f3. step1
2023-11-16 17:17:31.448 18931-18931 I/TagInline: exef3. func1
2023-11-16 17:17:31.448 18931-18931 I/TagInline: f3. step2
2023-11-16 17:17:31.448 18931-18931 I/TagInline: exef3. func2
2023-11-16 17:17:31.450 18931-19120 I/TagInline: exef3. func1
2023-11-16 17:17:31.451 18931-19121 I/TagInline: exef3. func1
标准库中的扩展函数: run,let,with,also,apply,takeIf,takeUnless
run, let, with 返回值是lambda表达式最后一行
- 若要返回lambda表达式最后一行,run最简洁
var item:Item = Item()
//run, let, with 返回值是lambda表达式最后一行
var result1:String = item.run {
a1 = "11"
a2 = 11
a3 = 11F
"11111111111111"
}
var result2:Int = item.let {
it.a1 = "222"
it.a2 = 200
it.a3 = 333F
100
}
var result3:Float = with(item){
a1 = "33"
a2 = 33
a3 = 333F
3F
}
also, apply 返回值是调用者自身
- 若要返回调用者自身,apply最简洁
//also, apply 返回值是调用者自身
var result4:Item = item.also {
it.a1 = "11"
it.a2 = 11
it.a3 = 11F
}
var result5:Item = item.apply {
a1 = "11"
a2 = 11
a3 = 11F
}
takeIf 及 takeUnless
- takeIf : 过滤条件满足,则返回自身,不满足返回null
- takeUnless : 过滤条件不满足,则返回自身,满足返回null
//takeIf : 过滤条件满足,则返回自身,不满足返回null
item.takeIf { it.a2 > 100 }?.run {
println(a2)
}
//takeUnless : 过滤条件不满足,则返回自身,满足返回null
item.takeUnless { it.a3 > 5F }?.run {
println(a3)
}
Nothing的用法
Nothing用于标记该函数永不返回
- 仅仅用于抛出异常的的函数,返回值可以用Nothing
//仅仅用于抛出异常的的函数,返回值可以用Nothing
fun nothingf1(): Nothing {
throw RuntimeException("nothingf1")
}
- 永远执行下去的函数,返回值可以用Nothing
//永远执行下去的函数,返回值可以用Nothing
fun nothingf2(): Nothing {
while (true){
}
}
Nothing是所有类型的子类型,Nothing可以作为泛型对象的临时空白填充
class CustomType<out T>{}
//Nothing是所有类型的子类型,Nothing可以作为泛型对象的临时空白填充
fun nothingCollection() {
//Nothing可以创建空白集合实例,作为不同类型集合变量的初始值
val emptyList: List<Nothing> = listOf()
var list1: List<Int> = emptyList
var list2: List<Float> = emptyList
val emptySet: Set<Nothing> = setOf()
var set1: Set<Int> = emptySet
var set2: Set<Float> = emptySet
val emptyMap: Map<String, Nothing> = mapOf()
var map1: Map<String, Int> = emptyMap
var map2: Map<String, Float> = emptyMap
//除了集合,其他泛型也可以
val emptyCustomType:CustomType<Nothing> = CustomType()
var customType1:CustomType<Int> = emptyCustomType
var customType2:CustomType<Float> = emptyCustomType
}
val关键字声明的变量只能赋值一次;
var关键字声明可更改的变量;
在输出语句中使用变量: ${变量名称}
var age = 365 * 8
println("You are already ${age}")
println("${age} is the very best age to celebrate!")
repeat创建循环语句: repeat(循环次数){}
repeat(10){
print("傻狗")
}
使用 IntelliJ IDEA 运行 Hello world : www.web3.xin/code/2230.h…
- 一开始犯的错,创建1个Kotlin的class,然后在里面写main函数.导致一直找不到run/运行 按钮.
- 应该创建一个Kotlin文件,不是class,直接在里面写main函数,可以运行
IntRange : 用于表示一组Int值的范围. 从A到B.
var i1 = 1..6
var i2:IntRange = 1..6
fun showRandom(){
println(i1.random())
println(i2.random())
//可以直接创建1个IntRange实例
println((1..6).random())
}
如果一个类要被继承,可以使用 open 关键字进行修饰
open class BaseActivity : AppCompatActivity() {}
class Jump : BaseActivity() {}
Kotlin和Java代码互转
- Java文件转Kotlin文件
- Code -> Convert Java File to Kotlin File
- Kotlin文件转Java文件
- Tools -> Kotlin -> Show Kotlin ByteCode
- Decompile
Class的写法
- Java中的Class实例,在Kotlin中写法是 ClassName::class.java
- Class实例作为方法参数,写法是 clazz:Class<*>
fun jump(clazz: Class<*>) {
val intent = Intent(this, clazz)
startActivity(intent)
}
fun J1(view: View) {
val clazz = MainActivity::class.java
jump(clazz)
}
静态函数
通过这个注解( @JvmStatic ), 将 object 和 companion object 的内部函数和属性,真正⽣生成为静态代码
open class T {
companion object{
@JvmStatic
open fun f1():Unit{
}
}
}
如何定义静态常量
在 object 中用 const val 生命静态常量
object ObjectTest {
const val a1:String = "111"
open fun f1():Unit{
}
}
//编译为Java代码
public final class ObjectTest {
@NotNull
public static final String a1 = "111";
@NotNull
public static final ObjectTest INSTANCE;
private ObjectTest() {
}
static {
ObjectTest var0 = new ObjectTest();
INSTANCE = var0;
}
}
如何定义静态函数
Kotlin实现静态方法与静态变量的两种方式
object ObjectTest {
@JvmStatic
open fun f1():Unit{
}
}
//编译为Java代码
public final class ObjectTest {
@NotNull
public static final ObjectTest INSTANCE;
@JvmStatic
public static void f1() {
}
private ObjectTest() {
}
static {
ObjectTest var0 = new ObjectTest();
INSTANCE = var0;
}
}
Elvis 操作符 : 可以通过 ?: 的操作来简化 if null 的操作
?: 后面可以是: 指定属性为null时候的缺省值, return, throw 1个异常
var date = lesson?.date ?: "星期一"
var date = lesson?.date ?: return
var date = lesson?.date ?: throw new Exception("***")
位运算符
Kotlin 运算符
Kotlin支持的位运算符同样有如下7个。
- and: 按位与。当两位同时为 1 时才返回 1。
- or: 按位或。只要有一位为 1,即可返回 1。
- inv: 按位非。单目运算符,将操作数的每个位(包括符号位)全部取反 。
- xor: 按位异或。当两位相同时返回 0,不同时返回 1。
- shl: 左移运算符。
- shr: 右移运算符。
- ushr: 无符号右移运算符。 Kotlin 的位运算符只能对 Int 和 Long 两种数据类型起作用。
首先左移和右移的区别是很好区分的
左移<< :就是该数对应二进制码整体左移,左边超出的部分舍弃,右边补零。举个例子:253的二进制码1111 1101,在经过运算253<<2后得到1111 0100。很简单
右移>> :该数对应的二进制码整体右移,左边的用原有标志位补充,右边超出的部分舍弃。
无符号右移>>> :不管正负标志位为0还是1,将该数的二进制码整体右移,左边部分总是以0填充,右边部分舍弃。
举例对比:
-5用二进制表示1111 1011,红色为该数标志位
-5>>2: 1111 1011-------------->1111 1110。
11为原有标志位1补充得到
-5>>>2: 1111 1011-------------->0011 1110。
左边部分总是以0填充 00为补充的0
循环控制
Kotlin 循环控制
for (int i = 1; i <= 10 ; i++) { }
for (int i = 1; i < 10 ; i++) { }
for (int i = 10; i >= 0 ; i--) { }
for (int i = 1; i <= 10 ; i+=2) { }
for (int i = 10; i >= 0 ; i-=2) { }
for (String item : collection) { }
for (Map.Entry<String, String> entry: map.entrySet()) { }
for (i in 1..10) { }
for (i in 1 until 10) { }
for (i in 10 downTo 0) { }
for (i in 1..10 step 2) { }
for (i in 10 downTo 0 step 2) { }
for (item in collection) { }
for ((key, value) in map) { }
字符串模板
- $字符串
- ${表达式(例如 if表达式)}
public fun f1():Unit{
var a:String = "1"
println("content is $a")
}
public fun f2():Unit{
var a: Array<String>? = null
println("content is ${if(a != null) a[0] else "a is null"}")
}
将很多特别小的类放到同一个文件中
c1.kt
open class c1{}
open class c2{}
open class c3{}
枚举类
- 在枚举类中定义任何方法,都要将枚举常量列表和方法定义用;分开
enum class Color1{
RED, GREEN, BLUE;
//在枚举类中定义任何方法,都要将枚举常量列表 和 方法定义 用 ; 分开
fun f1():Boolean{
return true
}
}
enum class Color2(var r:Int, var g:Int, var b:Int){
RED(255,0,0),
GREEN(0,255,0),
BLUE(0,0,255);
//在枚举类中定义任何方法,都要将枚举常量列表 和 方法定义 用 ; 分开
fun f1():Boolean{
return true
}
}
- 使用when处理枚举
fun gainColorStr(color:String) =
when(color){
Color.RED -> "RED"
Color.GREEN -> "GREEN"
Color.BLUE -> "BLUE"
}
when表达式
- when表达式带参数,则参数可以使用任何对象,when的分支条件也是对应类型对象
- when表达式不带参数,则when的分支条件就是任意的布尔表达式
fun whenFunc1(c1:Color, c2:Color) =
//when表达式可以使用任意类型对象做参数
when(setOf(c1,c2)){
//when的分支条件就是对应类型对象
setOf(Color.RED, Color.GREEN) -> "c1"
setOf(Color.RED, Color.BLUE) -> "c2"
else -> "errorColor"
}
fun whenFunc2(c1:Color, c2:Color) =
//when表达式不带参数
when{
(c1 == Color.RED && c2 == Color.GREEN) || (c1 == Color.GREEN && c2 == Color.RED) -> "color1"
(c1 == Color.RED && c2 == Color.BLUE) || (c1 == Color.BLUE && c2 == Color.RED) -> "color2"
else -> "errorColor"
}
- 在when表达式中完成 类型检查 和 类型转换
- kotlin使用is来进行类型检查
- kotlin中已经通过is的类型检查,则可以直接按照对应类型访问参数
open interface IC2
class I1(val name: String) : IC2
class I2(var age: Int) : IC2
fun whenFunc4(i: IC2): Int =
when (i) {
//1: is 相当于Java中的instanceOf
is I1 -> i.name.length
//2: when中已经使用is做了判断,后面就可以直接将参数当做对应类型来使用
is I2 -> i.age
else -> -1
}
- 使用代码块作为when的分支,则代码块最后一个表达式就是返回结果
fun whenFunc5(i:Int) =
when{
i > 100 -> {
Log.d("Tag","100")
//返回结果是100
100
}
i > 100 -> {
Log.d("Tag","50")
//返回结果是50
50
}
else -> {
Log.d("Tag", "else")
//返回结果是0
0
}
}
for循环遍历
- a..b 代表从a到b的闭合区间
- a<b
- a until b 代表从a到b的半闭合区间,即不包括b
- a<b
- a downTo b 代表从a到b的闭合区间
- a>b
- step x
- x>0 , 代表每次变化的量
fun funcFor1() {
for (i in 1..10) {
println("funcFor1: $i")
}
}
fun funcFor2(){
for(i in 1 until 10){
println("funcFor2: $i")
}
}
fun funcFor3(){
for(i in 10 downTo 1){
println("funcFor3: $i")
}
}
fun funcFor4(){
for (i in 1..10 step 2) {
println("funcFor4: $i")
}
}
fun funcFor5(){
for (i in 10 downTo 1 step 2) {
println("funcFor5: $i")
}
}
fun funcFor6(){
var arr:Array<Int> = arrayOf(4,3,6,0,17)
for(i in arr){
println("funcFor6: $i")
}
}
fun funcFor7(){
var map:HashMap<String,Int> = HashMap()
map["a1"] = 3
map["a2"] = 2
map.put("a3",1)
for ((k,v) in map){
println("funcFor7. $k -- $v")
}
}
fun funcFor8(){
println("funcFor8. start.")
for (i in 10 downTo -20 step 2) {
println("funcFor8: $i")
}
println("funcFor8. end.")
}
使用 in 来检查一个值是否在某个区间, 使用 !in 来检查一个值是否不在某个区间.
- in检查一个值是否在某个区间
- a in start..end
- !in检查一个值是否不在某个区间
- a !in start..end
- in可以作为when的分支使用
- when(p){in start..end -> {} else -> {}}
fun isLetter(c: Char): Boolean = c in 'a'..'z' || c in 'A'..'Z'
fun isNotDigit(c: Char): Boolean = c !in '0'..'9'
fun recognize(c: Char): String = when (c) {
in '0'..'9' -> {
Log.d("InTest","recognize. It's a digit")
"It's a digit"
}
in 'a'..'z', in 'A'..'Z' -> {
Log.d("InTest","recognize. It's a letter!")
"It's a letter!"
}
else -> {
Log.d("InTest","recognize. Error char!")
"Error char!"
}
}
try-catch
- Kotlin中,不会强制方法抛出指定异常.
- Kotlin中,try-catch是一个表达式,可以将它们赋值给一个变量.如果运行正常,变量的值就是try块中最后一行表达式,如果运行捕获到异常,变量的值就是catch块中最后一个表达式.
fun readNumber(reader:BufferedReader):Int{
//在Java中,必须添加try-catch块捕获IOException,或方法本身抛出该异常,但Kotlin不会强制
val line = reader.readLine()
retutn Integer.parseInt(line)
}
fun readNumber2(reader:BufferedReader){
val number = try{
Log.d("TryTest","readNumber2. try.")
//当执行无异常,则number赋值为最后一行表达式的值
Integer.parseInt(reader.readLine())
}catch(e:Exception){
Log.d("TryTest","readNumber2. catch.")
//当捕获到异常,则number赋值为321
321
}
println(number)
}
D/TryTest: readNumber2. try.
D/TryTest: readNumber2. catch.
I/System.out: 321
在Kotlin中创建集合
- 创建集合形式: XXOf
- XXOf创建的集合,实际就是原始的Java集合类型
fun createCollection(view: android.view.View) {
val r1 = setOf("1", "2", "3")
val r2 = hashSetOf("1", "2", "3")
val r3 = listOf(1, 2, 3)
val r4 = arrayListOf(1, 2, 3)
val r5 = arrayOf(1, 2, 3)
val r6 = mapOf(1 to 2, 2 to 4, 3 to 6)
val r7 = hashMapOf(1 to 2, 2 to 4, 3 to 6)
Log.d(
"CollectionTest",
"r1:" + r1.javaClass + " ; r2:" + r2.javaClass + " ; r3:" + r3.javaClass + " ; r4:" + r4.javaClass + " ; r5:" + r5.javaClass + " ; r6:" + r6.javaClass + " ; r7:" + r7.javaClass
)
}
//log
CollectionTest: r1:class java.util.LinkedHashSet ; r2:class java.util.HashSet ; r3:class java.util.Arrays$ArrayList ; r4:class java.util.ArrayList ; r5:class [Ljava.lang.Integer; ; r6:class java.util.LinkedHashMap ; r7:class java.util.HashMap
方法参数默认值 及 方法调用命名参数
- kotlin中声明方法,可以指定参数的默认值,对于指定了默认值的参数,调用方可以不传参数,直接使用默认值
- 参数默认值最大的好处,是避免了过多的方法重载.
- kotlin中在调用方法时,可以显式地声明参数名称,可以改变参数的原始排序,也可以不传入具有默认值的参数
//p1, p2, p3 都声明了默认值
fun <T> joinToString(
collection: Collection<T>,
p1: String = ",",
p2: String = "",
p3: String = ""
): String {
return "joinToString"
}
fun main() {
//1:因为joinToString方法参数有默认值,所以可以只传入部分参数,
//未传入的参数按照参数顺序使用方法声明的默认值
joinToString(setOf(1, 2), "p1", "p2", "p3")
joinToString(setOf(1, 2), ",")
joinToString(setOf(1, 2))
//2:如果在调用方法时,使用命名参数(显示地标明参数的名称),
//则可以不按照方法参数顺序填入参数,也可以省略任意参数,直接使用方法声明的任意值
joinToString(setOf(1, 2), p3 = "p3", p2 = "p2", p1 = "p1")
joinToString(setOf(1, 2), p2 = "p2")
}
静态方法及静态属性
- 在Java中,我们经常定义一些工具类,包含很多public静态方法,也会写一些静态属性(静态变量或静态常量).
- 在kotlin中,只需要创建一个XX.kt的kotlin文件:
- 在里面定义fun,定义的fun就属于静态方法.
- 在里面声明var或val属性,就是静态属性.
- var a:Int = 1
private static int a = 1;- val a:Int = 1
private static final int a = 1;- const val a:Int = 1
public static final int a = 1;
- 在XX.kt中定义的静态方法,在kotlin中调用,直接调用方法名称即可.
- 在XX.kt中定义的静态方法,在Java中调用
- 原始形式是 XXKt.funcName
- 若在XX.kt顶部添加注解@file:JvmName("FunctionUtils"),就可以改变kt文件生成的java类的名称.则Java中调用变成: FunctionUtils.joinToString 的形式.
@file:JvmName("FunctionUtils") package com.transsion.kiaction.c3 fun <T> joinToString( collection: Collection<T>, p1: String = ",", p2: String = "", p3: String = "" ): String { return "joinToString" }
扩展函数
- 扩展函数用于对指定类进行扩展,调用形式和该类普通成员函数一致.
- 扩展函数本质上是静态函数,不可重写
- 扩展函数的基本形式
- ClassName称为接受者类型. 指定被扩展的类或接口
- this称为接受者对象,指定类型的对象调用该扩展函数,该对象就是 this . this可以省略不写出来.
fun ClassName.funcName(params):ReturnType{ dealWith(this) return ReturnType } fun ClassName.funcName(params):ReturnType = ReturnType - 不同文件可能定义同名扩展函数,如果在1个文件中引用了同名扩展函数,在import时候添加 as 设置别名
import com.transsion.kiaction.c1.join as intJoin import com.transsion.kiaction.c3.join fun t(){ var set1 = setOf("1","2","3") set1.join() set1.intJoin(200) var set2 = setOf(1) var r:Int = set2.intJoin() }
可变参数
- Java中可变参数使用 Type... 形式.
- Kotlin中可变参数使用 vararg 参数名称:参数类型 形式
- Kotlin中可变参数和Java中一样,也是一个数组,若fun的参数类型是vararg,则调用方必须传入已经解包的数组,即将每个数组元素作为单独的参数.
- 比如一个变量本身已经是数组,则不可用传给参数类型是 可变参数的方法,必须对数组进行解包,使用 *数组 的形式.
fun f1VarArg(vararg params: String): Unit {}
fun f1(){
var args:Array<String> = arrayOf("1","2","3")
//对于数组,使用展开运算符 * ,将其中每个数组元素取出作为参数传入方法中.
val list1 = listOf("100",*args)
list1.forEach {
Log.d("ExpandY","it:"+it)
print(it)
}
val list2 = listOf("100",args)
list2.forEach {
Log.d("ExpandN","it:"+it)
print(it)
}
//会报错
//f1VarArg(args)
//必须使用展开运算符* , 将原始数组解包后使用
f1VarArg(*args)
/*
D/ExpandY: it:100
D/ExpandY: it:1
D/ExpandY: it:2
D/ExpandY: it:3
D/ExpandN: it:100
D/ExpandN: it:[Ljava.lang.String;@79fe14
* */
}
局部函数
- kotlin中,可以在函数内部继续声明函数,函数内部的函数称为局部函数.
- 局部函数可以访问其所在函数的所有参数.
class User(val name:String, val age:Int){}
fun localFunc(user:User):Unit{
//local就是局部函数
//local可以访问其所在函数:localFunc的所有参数
fun local():Boolean{
if(user.name==null){
return false
}
return true
}
if(local()){
Log.d("LocalFunc","经局部函数验证,参数合法!")
}else{
Log.d("LocalFunc","经局部函数验证,参数不合法!")
}
}
lateinit : 延迟初始化
表示变量无法在声明的时候就初始化,但在使用前肯定会进行赋值.加了lateinit,该变量的初始化完全靠开发者保证.
lateinit var a1:Int
Kotlin中,函数类型也是一种数据类型,可以被继承
/**
* Kotlin中,函数类型也是一种数据类型,可以被继承
*/
class IntegerFilter1(private val param: Float) : (Int) -> Boolean {
override fun invoke(p1: Int): Boolean {
return p1.isSmall(param) || p1.isTooSmall(param)
}
private fun Int.isSmall(param: Float): Boolean {
return this < param.toInt() && inRange()
}
private fun Int.isTooSmall(param: Float): Boolean {
return this < (param / 100F).toInt() && inRange()
}
private fun Int.inRange(): Boolean {
return this in 100..300
}
}
class IntegerFilter2(val p: Double) : (Int, Int) -> Boolean {
override fun invoke(p1: Int, p2: Int): Boolean {
return p1.f1(p) && p2.f1(p)
}
private fun Int.f1(param: Double): Boolean {
return this > param.toInt()
}
}
class IntegerFilter3 : (Int, Int) -> Boolean {
override fun invoke(p1: Int, p2: Int): Boolean {
return p1 > 100 && p2 > 100
}
}
运算符重载
Kotlin中,可以使用operator声明运算符重载. 格式是:
class Type{
operator fun 运算符(p1:Type1):Type2{
}
}
实例:
class OperatorC1 {
var a1: Int = 10
operator fun plus(param: OperatorC1): OperatorC1 {
val result = OperatorC1().apply {
a1 += param.a1
}
return result
}
operator fun minus(param: OperatorC1): OperatorC1 {
val result = OperatorC1().apply {
a1 -= param.a1
}
return result
}
companion object {
@JvmStatic
fun main(args: Array<String>) {
val p1 = OperatorC1()
val p2 = OperatorC1()
// + 对应着上面的plus
val p3 = p1 + p2
// - 对应着上面的minus
val p4 = p1 - p2
}
}
}
Kotlin支持的运算符重载包括:
| 代码调用形式 | operator声明的名称 |
|---|---|
| +a | a.unaryPlus() |
| -a | a.unaryMinus() |
| !a | a.not() |
| a++ | a.inc() |
| a-- | a.dec() |
| a + b | a.plus(b) |
| a - b | a.minus(b) |
| a * b | a.times(b) |
| a / b | a.div(b) |
| a % b | a.rem(b) |
| a .. b | a.rangeTo(b) |
| a in b | b.contains(a) |
| a !in b | !b.contains(a) |
| a[i] | a.get() |
| a[i, j] | a.get(i, j) |
| a[i_1, ..., i_n] | a.get(i_1, ..., i_n) |
| a[i] = b | a.set(i, b) |
| a[i, j] = b | a.set(i, j, b) |
| a[i_1, ..., i_n] = b | a.set(i_1, ..., i_n, b) |
| a() | a.invoke() |
| a(i) | a.invoke(i) |
| a(i, j) | a.invoke(i, j) |
| a(i_1, ..., i_n) | a.invoke(i_1, ..., i_n) |
| a += b | a.plusAssign(b) |
| a -= b | a.minusAssign(b) |
| a *= b | a.timesAssign(b) |
| a /= b | a.divAssign(b) |
| a %= b | a.remAssign(b) |
| a == b | a?.equals(b) ?: (b === null) |
| a != b | !(a?.equals(b) ?: (b === null)) |
| a > b | a.compareTo(b) > 0 |
| a < b | a.compareTo(b) < 0 |
| a >= b | a.compareTo(b) >= 0 |
| a <= b | a.compareTo(b) <= 0 |