-
lambda以及操作符
-
高阶函数以及操作符
-
Kotlin
泛型 -
集合操作
-
协程
-
操作符重载
-
潜规则
-
Kotlin文件和类不存在一对一关系
-
共生体
-
继承
-
修饰符
-
空指针问题
正文
潜规则
从Java转到kotlin,基本上都会存在java代码与kotlin共存的问题。而且为了快速转型,可能会直接把java类转成kotlin类,而这个过程中,涉及到java和kotlin的交互,往往会磕磕碰碰,以下总结了一部分 java kotlin交互方面的问题.
Kotlin文件和类不存在一对一关系
kotlin的文件,可以和类名一致,也可以不一致。这种特性,和c++有点像,毕竟c++的.h 和 .cpp文件是分开的。只要最终编译的时候对的上,文件名其实无所谓的。Java中,一个类文件的类名和文件名不一致,如果是public类,就会报异常。
在kotlin中,可以写成一致,如:
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=Kotlin(4&pos_id=img-XedzLoHP-1726962420312) java转kotlin潜规则.assets/image-20200103162656403.png)
不一致:
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=Kotlin(4&pos_id=img-bQ0RKyra-1726962420313) java转kotlin潜规则.assets/image-20200103162840686.png)
这样做的意义在于:
如果有很多个行数很短的类:在java中可能要占用大量的文件个数(Java中可以用内部类的形式解决),kotlin中则可以把这些类都放到同一个kt文件中,不用内部类也能解决。
共生体
Java中的静态 static关键字,在kotlin中不复存在,作为替换,Kotlin提出了共生体的概念。如果是kt文件去调用kt类的“静态”方法(不依赖对象),则要求后者的类结构中增加一个 companion object 成员变量。并且可以在 成员中写上 你想要定义的"静态"成员变量和成员方法
class Test001(_name: String) : Person(_name) {
companion object {
const val s: String = “”
const val s2: String = “”
fun t1(){
}
}
}
fun main(){
Test001.s
Test001.t1()
}
注:每一个kotlin类中,只能有一个共生体对象.
但是在java调用kt的"静态"成员方法时,必须带上共生体,但是,访问"静态"成员变量,则不能带:
public static void main(String[] args) {
Test001.Companion.t1();//Java访问kt的t1()共生体方法,必须带上Companion
String s2 = Test001.s;// 而访问共生体成员变量,不能带Companion
}
好纠结。为什么要这么设计。算了。查了一下kt反编译之后的Java源码:
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=Kotlin(4&pos_id=img-3sVbScJ6-1726962420314) java转kotlin潜规则.assets/image-20200103164440541.png)
共生体变成了Java类中的静态内部类,包含t1()方法。而s,s2 则是普通的静态变量。
修饰符
修饰符指的是 类 和 成员变量,成员方法 前面的 权限访问关键字。原 Java拥有 private ,protected,default ,public ,访问权限分别为: 本类内部,同包名或子类,同包名,全局。
然而,kotlin新增了一个概念,internal ,表示,相同Module内可访问,跨Module则不行。
并且,java和kotlin的 private ,protected,default ,public 的访问权限还有区别,但是我这里就不详述了,因为我觉得意义不大。能不能访问,写代码的时候编译器会告诉你,当场警告你,你就会修改代码。如果有问题。可以把kotlin Decompile成Java代码自己去对比试试。如有需要,后期再说吧。
空指针问题
通常要快速的将 旧java代码转化成kotlin代码,是拷贝java代码粘贴到kotlin文件内,让as自动转化,但是这种方式,容易造成很多空指针问题,有一些是很直白的报了编译错误,而有一些则是隐藏起来,等到程序运行时才会报错。直接报错的就不提了,下面演示隐藏的空指针问题:
Kotlin类:
class Student(name:String) {
var name: String = name
fun showName(tag: String) {
println(“$tag : $name”)
}
}
Java调用kt:
public class Main {
public static void main(String[] args) {
Student s = new Student(“zhou”);
s.showName(null);
}
}
此时,如果运行main函数,就会报出:
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=Kotlin(4&pos_id=img-ESVKxJ7X-1726962420314) java转kotlin潜规则.assets/image-20200103165613561.png)
告诉我们参数tag不可为null。但是奇怪的是,在java代码中,居然不会报编译错误。贼特么诡异。
解决方案:
在方法参数后面加上问号,变成这样:
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=Kotlin(4&pos_id=img-P5R5cmv9-1726962420314) java转kotlin潜规则.assets/image-20200103165838953.png)
没有基本数据类型
Kotlin之中没有基本数据类型,它只有:
Int,Short,Long,Float,Double,Byte ,Char,Boolean
这样的包装类型。
为什么没有?没有必要去纠结,但是只提供包装类型有一个好处,那就是 方便扩展函数的定义。
我们可以很轻松地对 Int,类型去扩展函数。
比如: Kotlin
自带了很多扩展函数:
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=Kotlin(4&pos_id=img-O0GQx9yF-1726962420315) java转kotlin潜规则.assets/image-20200103152004966.png)
这是系统定的,我们也可以自己来定义:
fun Int.plus100(): Int {//自定义扩展
return this + 100
}
fun main() {
val a: Int = 20
println(“${a.plus100()}”)
}
继承
在用kt重构部分模块的过程中,我发现频繁出现下面的问题:
Kotlin基类:
abstract class Person(name: String) {
var name: String? = name
}
Java子类:
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=Kotlin(4&pos_id=img-HKRkFG3P-1726962420315) java转kotlin潜规则.assets/image-20200103154351222.png)
由于我是从基础类开始重构,所以,在原先的Java代码中频繁出现了类似这种 访问权限不足的问题。一个一个去改成setName函数,工作量巨大。后来找到一个办法:
在kotin中加入 @JvmField 变成这样:
abstract class Person(name: String) {
@JvmField
var name: String? = name
}
@JvmField可以让 kotlin的成员属性变成公有的,kt转化成java时,会是如下这样:
public abstract class Person {
@JvmField
@Nullable
public String name;
public Person(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, “name”);
super();
this.name = name;
}
}
兼容原先的Java代码。不用大面积修改了。
默认支持可选命名参数
了解高级语言语法的同学肯定知道 可选命名参数和 可选位置参数,经测试: Kotlin的任何方法(包括构造方法和普通和方法),可以这么写:
fun test001(s: String, s1: String) {
println(“$s - $s1”)
}
fun main() {
test001(s = “1111”, s1 = “2222”) //卧槽,Kotlin默认支持 可选命名参数
}
这种特性可以很好的避免了Java中出现的一个方法包含N个参数 把人眼睛看花的情况:
private void test(String s1, String s2, String s3, String s5, String s6, String s7, String s8, String s9, String s10, String s11, String s12) {
//…
}
比如如上面所示,一个方法,有12个String参数,看多了会骂娘,谁特么写的。然而,用kotlin:
fun test(s1: String, s2: String, s3: String, s4: String, s5: String, s6: String, s7: String, s8: String, s9: String, s10: String, s11: String, s12: String) {}
s12) {
//…
}
比如如上面所示,一个方法,有12个String参数,看多了会骂娘,谁特么写的。然而,用kotlin:
fun test(s1: String, s2: String, s3: String, s4: String, s5: String, s6: String, s7: String, s8: String, s9: String, s10: String, s11: String, s12: String) {}