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扫描到传入参数为密封类时,则分支必须包括其全部子类,否则编译不通过。