包括基本的互操作行为、SAM转换、正则表达式、集合框架、IO操作:[实际上用到i的还是 java的api,只不过要注意kotlin为我们提供了很多扩展方法]、装箱和拆箱、注解处理器。
下面一一介绍:
1.基本的互操作行为:
1.属性读写:
Kotlin自动识别 Java Getter/Setter
Java操作Kotlin属性通过Getter/Setter
2. 空安全类型:
Kotlin 空安全类型的原理(java没这个。原理是Kotlin在编译的时候会增加一个函数调用,对参数类型或返回值类型进行为空的检查)
但是因为java没这个,所以kotlin想操作java的代码的话,就会遇到一个平台类型(Platform Type)的问题,这时候就需要我们自己明确
传入的参数或者返回值的类型是不是为空的。
当然开发Java代码的时候也可以使用注解@Nullable 和@NotNull来解决这个问题。
3.几类函数的调用:
包级函数:静态方法(Java没有。)【它在编译的时候会为kotlin生成一个类,这个类包含了所有的高级函数,在Java看来这些都是静态方法。所以在调用的时候只需要按照调用静态方法的方式去调用即可】
扩展方法: 带Receiver的静态方法。【只是增加了一个Receiver参数】
运算符重载: 带Receiver的对应名称的静态方法。
4.几个常见的注解的使用:
@JvmField: 将属性编译为Java变量。【如果我们希望kotlin里面的属性无论是看起来还是编译后都是像Java变量那样,就可以加该注解】
@JvmStatic: 将对象的方法编译成Java静态方法【对于Kotlin的伴生对象或者对象里面的方法,如果我们希望看起来真的像Java里面的静态方法的话,就可加该注解】
@JvmOverloads: 默认参数生成重载方法。【主要i是针对Kotlin中的默认参数。如果一个方法定义了一个默认参数,Java是不能看到它的,Java中并没有默认参数这个概念。如果想让Java享受到这个特性,就可以加该注解来标注这个方法。这样编译器就会为我们生成很多的重载方法,在Java中就可以方便的使用了】
@file: JvmName: 指定Kotlin文件编译后的类名。【像高级函数以及 在某一个文件中为类定义的扩展方法等等,他们在编译之后实际上都会被包含到一个类当中。在Java我们都会通过这个类来访问他们,这个类的名字默认是”Kotlin文件名Kt“,可以通过JVM类来指定这个文件编译后的名字】
5.NoArg 与AllOpen:
这2个插件。dataclass 因为它本身的属性导致它并没有一个默认的无参构造方法,这对于某些ORM框架或者像json这样的需要序列化和反序列化的框架来说是一个障碍。所以官方推出了NoArg插件,它可以在编译的时候为这些类生成一个默认无参构造方法。
其中,NoArg还支持Jpa注解,如 @Entity
而AllOpen插件 是为标注的类去掉final,允许被继承。因为在Kotlin中,所有的类默认是final,无法被继承。这对于像spring他们在编译的时候会为某些类生成它的子类,如果这个类是final的话,这个行为就不会被允许了。因此,官方推出了AllOpen这个插件。
其中,其支持Spring注解,如@Component
还可以支持自定义注解类型,例如@PoKo
6.泛型:【Koltin里面的泛型和Java里的泛型没有太大的差别】
不同之处:
通配符Kotlin的*对应于Java的? _____这是因为?在Kotion的作用太大了
协变和逆变 out/in _____由java中的extends和super变成了out/in
例如:ArrayList<out String >
没有Raw类型:_____在Java中Raw类型 的产生主要源于java 1.5之前根本没有泛型这个特性。【Kotlin没有这样的包袱了,所以没有Raw类型】
例如:像Java中的list这种不带泛型参数的写法就称之为Raw类型,对应于Kotlin里面就要写成List<*>
2.SAM转换
实例化Runnable的过程,反编译后会发现一个实例,这个实例实际上就是lambda表达式。
val samInJava = SAMInJava()
samInJava.addTask {
}
说明对于像Runnable这样的Java接口,它只有一个接口方法,那么我们可以在Kotlin中用一个lambda表达式来替代它。在编译的时候,编译器会帮我们把这个lambda表达式变成它。
例如:
Executors.newCachedThreadPool().submit {
}
SAM转换:
Single Abstract Method
SAM转换的条件:
java接口,单一接口方法。
注意转换后的实例变化。每次用lamda转换成runnable之后,runnable的实例都是不同的。
3.正则表达式
在Kotlin中用Raw字符串定义正则表达式。【比Java方便很多,少了很多的转义】
Java的Pattern在kotlin中也可以直接使用。
或者直接使用Kotlin中的Regex写出更符合kotlin风格的代码。
3.1 在Java中这么用正则表达式写一个例子:
public class Main {
public static void main(String... args) {
String source = "Hello, This my phone number: 010-12345678. ";
String pattern = ".*(\\d{3}-\\d{8}).*";
Matcher matcher = Pattern.compile(pattern).matcher(source);
while(matcher.find()){
System.out.println(matcher.group());
System.out.println(matcher.group(1));
}
}
}
3.2 在Kotlin中的等效的写法:(照抄的java)
fun main(args: Array<String>) {
val source = "Hello, This my phone number: 010-12345678. "
val pattern = """.*(\d{3}-\d{8}).*"""
val matcher = Pattern.compile(pattern).matcher(source)
while (matcher.find()) {
println(matcher.group())
println(matcher.group(1))
}
}
3.3 kotlin也有自己的方式处理正则表达式:[kotlin自身的正则表达式]
fun main(args: Array<String>) {
val source = "Hello, This my phone number: 010-12345678. "
val pattern = """.*(\d{3}-\d{8}).*"""
Regex(pattern).findAll(source).toList().flatMap(MatchResult::groupValues).forEach(::println)
}
findAll()返回的是一个软序列。flatMap(MatchResult::groupValues)相当拿到了一个groupvalues的集合。Kotlin对上面的转义的支持,比如java中需要把\d中的斜杠转移正正则表达式中的斜杠,即写为//d。但是kotlin中有RawString,所以上面的代码不用考虑转义斜杠了。
4.集合框架
kotlin没有自己造轮子,所以它可以用java里面中的所有东西。
比如:java中的一个问题,我们remove(0),假设这个list存放的是整型,那我们到到底是remove索引为0的元素还是remove掉list中0这个整型对象呢。
针对此,kotlin改善了下。通过索引remove掉,就写为lsit.removeAt(int index).;list.remove(obejct)
【::println是针对println函数的引用。其他的以次类推。】
集合注意可变和不可变。【不可变的集合返回的是kotlin内部它自定义的一个集合。因此我们无权对它进行put等操作。但是Java调用不可变的时候它统一可变的,假设此时在java中调用它并add等操作就会报错,因为它返回的List根本就没实现add和put等操作】
这些可变和不可变的集合都会被映射成java中对应的集合。
需要注意的细节:
Kotlin到Java的类型映射。
不可变与可变集合接口。
kotlin对这些集合框架部分接口做了优化。比如remove和removeAt
5.IO操作
File、Stream、Reader、Writer的扩展方法.
使用use扩展自动关闭资源。
小文件readLines()、readText()、readBytes()一次性读写操作。
5.1 用java读写一个文件的例子:
public class IO {
public static void main(String... args) {
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader(new File("build.gradle")));
String line;
while((line = bufferedReader.readLine()) != null){
System.out.println(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5.2 Kotlin照抄上面的代码:
fun main(args: Array<String>) {
val file = File("build.gradle")
val bufferedReader = BufferedReader(FileReader(file))
var line: String
while(true){
line = bufferedReader.readLine()?:break
println(line)
}
bufferedReader.close()
}
注意line = bufferedReader.readLine()?:break中的line = bufferedReader.readLine()它实际上是一个赋值语句,它并没有把line的值作为返回值。这个和java不一样。所以不能在while中直接写 while((line = bufferedReader.readLine()) != null),所以上面的代码直接写为while(true)了。
5.3 Kotlin中的先进写法:【传统写法常常忘记close】
解决这个问题,Closeable可以为 我们自动关闭,use实际上就是这个东西的扩展方法了。它可以为我们closeable接口的实例来提供自动关闭的操作。
fun main(args: Array<String>) {
val file = File("build.gradle")
BufferedReader(FileReader(file)).use {
var line: String
while(true){
line = it.readLine()?:break
println(line)
}
}
}
所以写在use里面的不用我们去手动关闭:
对于一些小文件,kotlin提供了一些更简单的方法,例如:
fun main(args: Array<String>) {
File("build.gradle").readLines().forEach(::println)
}
6.装箱和拆箱
了解kotlin基本类型到java的映射关系。【映射是编译器自动帮我们去做的】
注意规避基本类型相关的的问题【因为kotlin不再区分java中的基本类型和装箱类型】
kotlin中的int 类型和Integer装箱类型 合二为一变为 ”Int“(float、 double也是)
.java:
public interface BoxIf1 {
String get(int key);
String get(Integer key);
}
在kotlin中去实现它:
class BoxImpl1: BoxIf1 {
override fun get(key: Int): String {
return "Hello"
}
// override fun get(key: Int?): String {
// return "Hello"
// }
}
看上去没什么问题。
再看另外一个接口:
public interface BoxIf2 extends Map<Integer, String> {
String get(int key);
String get(Integer key);
}
然后kotlin中去实现它:
class BoxImpl2: BoxIf2 {
override val size: Int
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override fun containsKey(key: Int?): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun containsValue(value: String?): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun get(key: Int): String? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override operator fun get(key: Int): String? {
return "Hello"
}
override fun isEmpty(): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override val entries: MutableSet<MutableMap.MutableEntry<Int, String>>
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override val keys: MutableSet<Int>
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override val values: MutableCollection<String>
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override fun clear() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun put(key: Int?, value: String?): String? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun putAll(from: Map<out Int, String>) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun remove(key: Int?): String? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
发现会爆红。
因为kotlin中的Map把get()_方法变成了operator方法,并且参数Key和java中有不同之处。java中的get()的参数是object。这实际上是kotlin编译器为我们做的一个优化。所以在此处没办法写出一个合适的方法满足这个接口。
override fun get(key: Int): String? {
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
// }
//
// override operator fun get(key: Int): String? {
// return "Hello"
// }
//
这2个get方法冲突。所以用koitlin不能直接去实现这个接口。
这个时候只能写一个中间的java类(复写get方法,那么kotlin就不会去访问它了)。让kotlin直接去继承这个类来解决这个问题:
public abstract class AbsBox implements Map<Integer, String> {
public String get(Object key) {
return null;
}
}
kotlin去继承这个中间类:
class BoxImpl3: AbsBox() {
override fun get(key: Int): String {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override val size: Int
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override fun containsKey(key: Int?): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun containsValue(value: String?): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun isEmpty(): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override val entries: MutableSet<MutableMap.MutableEntry<Int, String>>
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override val keys: MutableSet<Int>
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override val values: MutableCollection<String>
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override fun clear() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun put(key: Int?, value: String?): String? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun putAll(from: Map<out Int, String>) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun remove(key: Int?): String? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
7.注解处理器
apply plugin: "kotlin-kapt"
注意添加生成的代码到SourceSets、
注意IntelliJ有些还不支持注解处理器的编译,需要在右侧的gradle手动点击build。
interface GitHubService{
@GET("/repos/enbandari/Kotlin-Tutorials/stargazers")
fun getStarGazers(@Query("page") page: Int = 1, @Query("per_page") pageSize: Int = 20): Call<List<User>>
}
object Service {
val api: GitHubService by lazy {
DaggerRESTFulComponent
.builder()
.build()
.retrofit()
.create(GitHubService::class.java)
}
}
fun main(args: Array<String>) {
Service.api.getStarGazers().execute().body().map(::println)
}
上述代码Dagger要i使用到注解处理器:
在build.gradle中添加:apply plugin: "kotlin-kapt"
然后build,可以在build文件夹下为我i们生成了很多文件。
并在build.gradle中添加这些文件的路径:
sourceSets{ main.kotlin.srcDirs += "build/generated/source/kapt/main"}