0
点赞
收藏
分享

微信扫一扫

Kotlin学习第二天

驚鴻飛雪 2022-04-17 阅读 55

Kotlin学习第二天

笔者之前记录了Kotlin的入门,读者可看这篇文章

as关键字-类型强转

as类型强转,例如

val activity = context as Activity

静态函数的声明

object类中的类似静态方法的调用

使用Kotlin定义Object类,其内部的方法调用类似于static方法的调用,但其并非是真正的静态方法

创建object Utils,Kotlin实现如下:

object Utils {
    fun test() {

    }
}

调用如下:

Utils.test()

其对应的java文件如下:

public final class Utils {
   @NotNull
   public static final Utils INSTANCE;

   public final void test() {
   }

   private Utils() {
   }

   static {
      Utils var0 = new Utils();
      INSTANCE = var0;
   }
}

那为何调用类似于静态方法的调用呢,这只是Kotlin提供的语法糖,Utils.test()对应的java代码为Utils.INSTANCE.test();

companion object

使用object则其内部的方法都会变为类似静态方法,那如果只想让某些方法呢,借助companion object则可实现

Kotlin文件如下:

class Test {
    fun test1() {
        
    }
    companion object {
        fun test2() {
            
        }
    }
}

其对应的Java如下:

public final class Test {
   @NotNull
   public static final Test.Companion Companion = new Test.Companion((DefaultConstructorMarker)null);

   public final void test1() {
   }

   ...
   //静态内部类
   public static final class Companion {
      public final void test2() {
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

可发现其类内部存在一个Companion的静态内部类,还持有一个此类的静态对象,调用test2(),本质调用的是此对象的test2()

Test.test2()   //对应java代码:Test.Companion.test2();

@jvmStatic

若真的想使用静态方法,则必须借助@jvmStatic,其只能注解在companion object内部的方法上

class Test {
    fun test1() {

    }
    companion object {
        @JvmStatic
        fun test2() {

        }
    }
}

其对应的java文件如下:

public final class Test {
   @NotNull
   public static final Test.Companion Companion = new Test.Companion((DefaultConstructorMarker)null);

   public final void test1() {
   }
	
    
   //声明静态方法
   @JvmStatic
   public static final void test2() {
      Companion.test2();
   }

   ...
   public static final class Companion {
      @JvmStatic
      public final void test2() {
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

发现上面多出来test2()的静态方法,其内部还是会调用Companion对象的非静态test2()

调用上述方法:

Test.test2() //对应的java代码为Test.Companion.test2();

发现其并没有调用生成的静态方法,而是调用的上述静态内部类的非静态方法,如此设计的原因主要是兼容java的调用,如果在纯java中调用此方法则会调用到真正的静态方法,若我们的代码不需要java调用,则没有必要使用@jvmStatic。

顶层方法

在Kotlin文件直接写方法会编译成静态方法

直接创建Kotlin文件,注意不是类,是纯Kotlin文件,命名为HelloWorld

fun main() {

}
fun test() {
    
}

其反编译的java代码为

public final class HelloWorldKt {
   public static final void main() {
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }

   public static final void test() {
   }
}

此时发现test()为静态方法

调用如下:

Kotlin中直接test()即可

test();

若是java则需使用类调用,Kotlin文件最终会编译成类

HelloWorldKt.test();

静态变量的声明

与静态方法类似的,有三种方式

object类中定义

定义object Test

object Test {
    val value = 1
}

此种方式只能在Kotlin中调用,Java不能调用

查看Test字节码反编译的java文件

public final class Test {
   private static final int VALUE;
   @NotNull
   public static final Test INSTANCE;

   public final int getVALUE() {
      return VALUE;
   }

   private Test() {
   }

   static {
      Test var0 = new Test();
      INSTANCE = var0;
      VALUE = 1;
   }
}

在Kotlin中调用

Test.VALUE //其反编译的java代码为Test.INSTANCE.getVALUE();

java没有此语法糖,因此无法调用,想要使用必须使用以下写法

Test.INSTANCE.getVALUE();

若不想使用以上写法,则可借助const

object Test {
    const val VALUE = 1
}

其反编译的value为下,由private变为public

public static final int VALUE = 1;

在任何地方都可调用

注意 const只能在object和companion object中使用

companion object定义静态变量

区别和上面不大,读者可自行分析,const可在java中直接调用,无const在java需借助内部的静态对象的getValue方法

顶层定义静态变量

在HelloWorld.kt中定义变量

val value = 1

对应反编译的java代码为

public final class HelloWorldKt {
   private static final int value = 1;

   public static final int getValue() {
      return value;
   }
}

在Kotlin中调用 直接引用变量名字即可

在java中需以下使用:

HelloWorldKt.getValue();

标准函数的使用

let

let主要用于对象的辅助判空操作,前篇文章已经讲述

with

with接收两个参数,一个任意类型的对象,一个Lambda表达式,第一个参数会传给Lambda使用,其Lambda内部执行的方法都是传入的对象所执行的,Lambda的最后一行代码会当成返回值返回,调用with则Lambda会立即执行。

val result = with(obj) {
    //this则代表obj
    //test() 等价于 this.test() 等价于 obj.test()
    //返回值,result最终为value
    "value"
}

比如存在一个水果列表,现在想吃完所有水果,并打印结果,不借助with如下:

val list = listOf("apple, banana, orange")
val builder = StringBuffer()
builder.append("Start eating fruits.\n")
for (fruit in list) {
    builder.append(fruit).append("\n")

}
builder.append("Ate all fruits")
val result = builder.toString()
println(result)

with实现如下:

val list = listOf("apple, banana, orange")
val result = with(StringBuilder()) {
    append("Start eating fruits.\n")
    for (fruit in list) {
        append(fruit).append("\n")
    }
    append("Ate all fruits")
    toString()
}
println(result)

可以看出我们可以省略对象去调用方法,使得代码更加简洁

run

run与with的使用场景类似,不同的是run在对象上调用,且只需要一个Lambda参数,其他地方一样。

上述吃水果用run实现如下:

val list = listOf("apple, banana, orange")
val result = StringBuilder().run {  
    append("Start eating fruits.\n")
    for (fruit in list) {
        append(fruit).append("\n")
    }
    append("Ate all fruits")
    toString()
}
println(result)

apply

apply与run相似,其也需在对象上调用,不同的是他的返回值是调用对象本身。

上述吃水果使用apply实现如下:

val list = listOf("apple, banana, orange")
val result = StringBuilder().apply {
    append("Start eating fruits.\n")
    for (fruit in list) {
        append(fruit).append("\n")
    }
    append("Ate all fruits")
}
println(result.toString())

用Kotlin实现的页面跳转:

val intent = Intent(this, OtherActivity::class.java);
intent.putExtra("param1", "data1");
intent.putExtra("param2", "data2");
startActivity(intent)

借助上述标准函数则可如下:

val intent = Intent(this, OtherActivity::class.java).apply {
    putExtra("param1", "data1");
    putExtra("param2", "data2");
}
startActivity(intent)

kotlin不需要使用findViewById

bulid.gradle 开头导入’kotlin-android-extensions’

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
}

在后续使用时则可直接使用id拿到相应控件,例如activity_main中存在一个id为myButton的Button,使用则可直接引用myButton,但一定要导入import kotlinx.android.synthetic.main.activity_main.*

myButton.setOnClickListener { 
    Log.d("MainActivity", "myButton click")
}

inner关键字-普通内部类的声明

在类的内部使用inner定义类

inner class Test() {

}

生成的反编译java代码为

public final class Test {
    
}

懒加载-lateinit

先看一段代码:

var file: File? = null
fun main() {
    file = File("路径")
    file?.name
    file?.toURI()
}

由于某些原因,我们不会在变量声明的时候直接初始化,此时此对象只能赋为null,由于Kotlin的空指针检查,则必须加上?才能编译通过,在后续访问此对象的变量和方法都要加上?,很不方便。

想要解决上述问题则需借助lateinit关键字

lateinit var file: File
fun main() {
    file = File("路径")
    file.name
    file.toURI()
}

若未初始化则直接抛出UninitializedPropertyAccessException异常

Kotlin提供了语法检查此对象有无初始化

lateinit var file: File
fun main() {
    if (!::file.isInitialized) {
        file = File("路径")
    }
    file.name
    file.toURI()
}

::为固定语法,记住即可

密封类的使用-sealed

先看一段代码:

获取返回值

定义Result接口

interface Result 
class Success(val msg: String) : Result
class Failure(val msg: String) : Result

定义一个方法

fun getResultMsg(result: Result) = when(result) {
    is Success -> result.msg
    is Failure -> result.error.message
    else -> throw IllegalArgumentException()
}

由于Kotlin的性质,else必须写,但以上代码只可能由两种情况else纯属多余。若程序员疏忽传入了UnKown类则APP必崩溃。

要想解决上述问题则需借助sealed,密封类及其子类只能定义在同一个文件的顶层位置,不可嵌套在其它类中

修改上述代码:

sealed class Result
class Success(val msg: String) : Result()
class Failure(val error: Exception) : Result()

方法修改如下:

fun getResultMsg(result: Result) = when(result) {
    is Success -> result.msg
    is Failure -> result.error.message
}

此时去掉else即可,当when扫描到传入参数为密封类时,则分支必须包括其全部子类,否则编译不通过。

举报

相关推荐

RHCSA学习第二天

java学习 第二天

学习python第二天

学习HTML第二天

JAVA学习第二天

rust学习(第二天)

JS学习第二天

JavaScript学习第二天

0 条评论