本文主要是介绍Kotlin,简直是 Java 的 Pro Max!(笔记2),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
as - 类型转化
静态方法的声明
object 修饰类中所有方法类似 “静态”
companion object 修饰个别方法类似 “静态”
@JvmStatic 修饰一个方法为静态方法
顶层方法为静态
静态变量的声明
object 类中定义
companion object 定义静态变量
顶层定义静态变量
标准函数
let
with
run
apply
inner - 非静态内部类声明
lateinit 懒加载
sealed 密封类
as - 类型转化
例如 List 从 可变到不可变.
val a = mutableListOf(1, 2, 3)val b = a as List<Int>
静态方法的声明
object 修饰类中所有方法类似 “静态”
和 Java 一样通过类名就可进行对方法的调用,但其并非是真正的静态方法.
object Test {fun test() {println("我并非是真的静态方法~")}
}fun main() {Test.test()
}
对应的 Java 文件如下:
实际上,这只是 Kotlin 提供的语法糖,通过静态代码块初始化内部持有的静态 Test 对象.
companion object 修饰个别方法类似 “静态”
如果我们只想让某些方法变成类似静态方法,就可以通过 companion object 实现
class Test {companion object {fun test1() {println("我是静态的")}}fun test2() {println("我是非静态的")}
}fun main() {Test.test1()
}
Java 如下:
public final class Test {@NotNullpublic static final Companion Companion = new Companion((DefaultConstructorMarker)null);public final void test2() {String var1 = "我是非静态的";System.out.println(var1);}public static final class Companion {public final void test1() {String var1 = "我是静态的";System.out.println(var1);}private Companion() {}// $FF: synthetic methodpublic Companion(DefaultConstructorMarker $constructor_marker) {this();}}
}public final class TestKt {public static final void main() {Test.Companion.test1();}// $FF: synthetic methodpublic static void main(String[] var0) {main();}
}
实际上是 Test 类内部存在一个 Companion 的静态内部类,而在 Test 中又 new 了一个 Companion 对象,并修饰为 static.
因此调用的时候是通过 Test 类名调用其中的静态对象 Companion,在该对象调用内部的非静态方法 test1().
@JvmStatic 修饰一个方法为静态方法
要想使用真正的静态方法,必须要通过 @JvmStatic,并且其注解只能在 companion object 内部的方法上.
public final class Test {@NotNullpublic static final Companion Companion = new Companion((DefaultConstructorMarker)null);public final void test2() {String var1 = "我是非静态的";System.out.println(var1);}@JvmStaticpublic static final void test1() {Companion.test1();}public static final class Companion {@JvmStaticpublic final void test1() {String var1 = "我是静态的";System.out.println(var1);}private Companion() {}// $FF: synthetic methodpublic Companion(DefaultConstructorMarker $constructor_marker) {this();}}
}
可以发现有两个 test1() 方法,一个是 Test 类的 test1 静态方法,一个是静态内部类中的非静态方法,调用时是通过调用 Test 中的静态方法,进而在静态方法内部继续调用静态内部类中的非静态方法.
这样设计主要是为了兼容 Java 的调用. 如果 Java 和 Kotlin 混写,需要在 Java 代码中调用到真正的静态方法,才需要使用该注解,否则没必要.
顶层方法为静态
在 Kotlin 文件中的类外直接写变量会变成静态的,方法也是如此.
创建 kt 文件,命名为 Solution,然后直接写一个 test 方法,如下:
fun test() {}
对应的 Java 代码如下:
public final class SolutionKt {public static final void test() {}
}
静态变量的声明
object 类中定义
Kotlin 如下
object Solution {val aaa = 1}
对应 Java 如下:
public final class Solution {private static final int aaa;@NotNullpublic static final Solution INSTANCE;public final int getAaa() {return aaa;}private Solution() {}static {Solution var0 = new Solution();INSTANCE = var0;aaa = 1;}
}
可以看到,这里的 aaa 也是通过静态代码块初始化的.
如果 Java 想要调用该类中的静态变量 aaa,需要通过 public 的 instance 实例才能调用到,如果在 Java 中想直接调用,可以通过 const 来解决.
object Solution {const val aaa = 1
}
对应的 Java 如下:
public final class Solution {public static final int aaa = 1;@NotNullpublic static final Solution INSTANCE;private Solution() {}static {Solution var0 = new Solution();INSTANCE = var0;}
}
可以看出,通过 const 直接将 aaa 修饰成 public.
companion object 定义静态变量
和上面的 object 定义几乎没区别.
class Solution {companion object {val aaa = 1}
}
对应的 Java 代码:
public final class Solution {private static final int aaa = 1;@NotNullpublic static final Companion Companion = new Companion((DefaultConstructorMarker)null);public static final class Companion {public final int getAaa() {return Solution.aaa;}private Companion() {}// $FF: synthetic methodpublic Companion(DefaultConstructorMarker $constructor_marker) {this();}}
}
另外 const 修饰后可在 Java 中直接调用,没有 const 在 Java 中需要借助静态对象获取.
顶层定义静态变量
在 Solution.kt 中直接定义全局变量
val aaa = "hello"
对应 Java 如下:
public final class SolutionKt {@NotNullprivate static final String aaa = "hello";@NotNullpublic static final String getAaa() {return aaa;}
}
标准函数
let
let 是一个函数,提供了函数式 API 接口,会将调用者作为参数传递到 Lambda 表达式,调用之后会立马执行 Lambda 表达式的逻辑.
aaa.let { it -> // it 就是 aaa(调用者)//执行业务逻辑
}
作用:主要用于对象辅助判空操作
fun test(name: String?) {//不为空才执行内部逻辑name?.let {println("name: $it") //it 就是 name}}fun main() {test(null)
}
with
with 用来接收两个参数,一个是任意类型,另一个是 Lambda 表达式,第一个参数会传给 Lambda 使用.
Lambda 内部执行的方法都是 with 的第一个参数所提供的.
Lambda 的最后一行代码会当成返回值返回.
class Test {fun test1() {println("test1")}fun test2() {println("test2")}
}fun main() {val result = with(Test()) {//this 就表示 obj,一般省略 thistest1()test2()"ok"}println(result)
}
作用:主要用于简化对一个对象中多个方法的调用,避免在每次调用方法时都重复写对象名. 使用场景通常是需要对一个对象进行多个操作,
例如如下场景可读性提高了不少
data class Student(val name: String,val age: Int,val gender: Int
)fun main() {val student = Student("cyk", 18, 1)val result = with(student) {println("name: $name, age: $age, gender: $gender")"ok"}
}
run
run 与 with 的使用场景类似,唯一不同的是 run 是在对象上调用,且需要一个 Lambda 参数
data class Student(val name: String,val age: Int,val gender: Int
)fun main() {val student = Student("cyk", 18, 1)val result = student.run {println("name: $name, age: $age, gender: $gender")"ok"}
}
如果说 with 和 run 到底有什么使用场景上的区别,那么就是:
- with 更强调对对象的属性方法的带入一个代码块中,使得代码可读性提高
- run 常常用于创建一个临时的作用域,并在这个作用域中执行一些操作,然后返回结果,提高代码可读性.
run 实际上也经常这样玩(和 with 就区别开了):
val result = run {val a = 1val b = 2a + b}
apply
apply 和 run 相似. 不同的是必须要要在对象上调用,并且返回值是调用对象本身.
val list = listOf("beef", "fish", "egg")//吃东西val result = StringBuilder().apply {append("开始吃")list.forEach { str ->append(str)}append("吃完了")}
inner - 非静态内部类声明
inner 用于修饰内部类为非静态的
Kotlin 中内部类默认是静态的,这意味在着他们没有外部类实例的隐式调用,减少了内存泄露的可能性. 然而,如果一定要在内部类中访问外部类的成员,就可以使用 inner 关键字来声明内部类,使其变为非静态的.
Ps:为什么静态内部类可以减少内存泄露的可能?因为静态内部类无法持有外部类的隐式调用. 换言之,如果一个内部类不是静态的,那么他会持有外部类的隐式调用,也就意味着,只要内部类实例存在,外部类的实例就不会被垃圾回收器回收.
例如如下示例,Student 中调用就可以拿到 name,而 Teacher 中则不可以.
class Human {private val name = 1inner class Student {fun test () {println("name: $name") //正确}}class Teacher {fun test() {println("name: $name") //报错}}}
Kotlin 示例如下:
class Human {inner class Student {}class Teacher {}}
对应的 Java 如下:
public final class Human {public final class Student {}public static final class Teacher {}
}
lateinit 懒加载
a)写 Kotlin 的时候可能会常常遇到一个问题:“ 就是有时候我们并不想给某个变量在声明的时候就进行初始化,此时只能给这个对象赋值为 null,又由于 Kotlin 的空检查,类型必须加上 ? 才能编译通过,很不方便.
如下代码:
fun test(): String {var result: String? = nullif(true) {result = "yes"} else {result = "no"}return result
}
想要解决上述问题,就需要使用 lateinit 关键字:
fun test(): String {lateinit var result: Stringif(true) {result = "yes"} else {result = "no"}return result
}
Ps:上述代码中,若函数结束还未初始化 result,就会抛出 UninitializedPropertyAccessException 异常
b)另外,Kotlin 提供了语法检查此对象是否有初始化
lateinit var result: String
fun test(): String {if(!::result.isInitialized) { //未初始化result = "yes"} else { //已初始化result = "no"}return result
}
此处的 :: 为固定写法,记住就行.
sealed 密封类
a)先来看一个栗子:
定义 result 接口
interface Result
class Success(val code: Int): Result
class Failure(val code: Int): Result
方法如下:
fun getResultCode(result: Result) = when(result) {is Success -> {println("ok")result.code}is Failure -> {println("非法 code: ${result.code}")result.code}else -> throw IllegalArgumentException()
}
由于 Kotlin 的 when 特性,else 必须写,但是上述有些情况 else 纯属多余.
想要解决上述问题,需要借助 sealed,密封类以及子类只能定义在同一个文件的顶层位置,不可嵌套在其他类中.
b)使用密封类修改上述代码:
sealed class Result
class Success(val code: Int): Result()
class Failure(val code: Int): Result()fun getResultCode(result: Result) = when(result) {is Success -> {println("ok")result.code}is Failure -> {println("非法 code: ${result.code}")result.code}
}
此时就可以去掉 else,当 when 传入参数为密封类时,则分支必须包含其全部子类,否则编译不通过.
这篇关于Kotlin,简直是 Java 的 Pro Max!(笔记2)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!