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
扫描到传入参数为密封类时,则分支必须包括其全部子类,否则编译不通过。
✨ 原 创 不 易 , 还 希 望 各 位 大 佬 支 持 一 下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下
👍 点 赞 , 你 的 认 可 是 我 创 作 的 动 力 ! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!
⭐️ 收 藏 , 你 的 青 睐 是 我 努 力 的 方 向 ! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!
✏️ 评 论 , 你 的 意 见 是 我 进 步 的 财 富 ! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!