前篇系列文章(请同学们按照顺序来学习):
一起从零学Kotlin-20170728:
http://blog.csdn.net/clandellen/article/details/76283527
一起从零学Kotlin-20170730:
http://blog.csdn.net/clandellen/article/details/76369434
一起从零学Kotlin-20170801:
http://blog.csdn.net/clandellen/article/details/76519661
一起从零学Kotlin-20170803:
http://blog.csdn.net/clandellen/article/details/76602453
链接:http://pan.baidu.com/s/1geVxlJ1 密码:4nht
官方API才是学习的王道……
Intellij IDEA(kotlin开发学习IDEA,并且还支持Java)下载
https://www.jetbrains.com/idea/
官网下载慢的可以从我云盘里面下载:
链接:http://pan.baidu.com/s/1i4Au2nb 密码:qato
Java和Kotlin可以混合开发,这你肯定是知道的,但是要想Java与Kotlin混合开发规则又是怎么样的呢?本篇文章带你进行学习。
Kotlin能自动识别Java中类属性的Getter和Setter,Java操作Kotlin属性通过Getter / Setter,这没什么难点。
Kotlin的空类型是怎样处理的?前面已经讲解了,实际Kotlin会实时的运行一个方法,对变量,参数,返回值等等进行空类型检查,所以kotlin操作Java代码必须分析是否Java的那个变量,参数,返回值能否为空,因为Java不存在Kotlin这种检查空类型的机制。所以需要开发者自己去分析某些位置能不能为空,当然也可以通过Java注解的方式来避免这一问题。@Nullable和@NotNull
在Kotlin中有一个Java不存在的类型的函数,这种函数就是包级函数,包级函数的所有函数(包括扩展函数)都是静态的,在编译的时候呢,Kotlin就把所有的包级函数都放在了一个类当中,Java可以通过此类来访问到所有的包级函数,这个类的名字默认的就是: Kotlin的文件名+kt,当然也可以通过注解@file:JvmName的方式进行修改,示例代码如下:
包名a.kt
//包名a.kt代码
class Student(var name:String,var age:Int)
fun Student.getMessage(){//这个扩展方法被编译成类"包名akt"中的静态方法public static getMessage(Student student)
println("学生姓名:${this.name},学生年龄:${this.age}")
}
fun method(i:Int){//这个函数被编译类"包名akt"中的静态方法public static method(int i)
println(i)
}
Java中调用:
public static void main(String[] args){
Student s = new Student("ellen",23);
包名aKt.getMessage(s);//访问包名akt的扩展函数
包名aKt.method(3);//访问包名akt的扩展函数
//还有运算符的重载,这跟扩展方法调用是一样的,注意Java中不能通过运算符进行调用,而是要通过方法的名字
}
1.@JavaFiled:将属性编译为Java变量
2.@JvmStatic:将对象的方法编译成Java静态方法,通常应用与伴生对象
3.@JvmOverloads:默认参数生成重载方法
4.@file:JvmName:指定Kotlin文件编译后的类名
这在前面讲到”data”关键字已经进行了说明,被”data”修饰的类是final的,并且是没有无参构造函数,这感觉是个坑,所以Kotlin官方给出了解决方案,就是使用NoArg(无参构造函数)和AllOpen(可被继承)来解决问题。
1.Kotlin中的类型通配符不是”?”了,而是”*”,因为”?”在Kotlin当中有其他用途,你已经学过啦
2.协变与异变 out/in
3.没有Raw类型
其他是一样一样的,用法是一样的。
SAM 转换,即 Single Abstract Method Conversions,就是对于 只有单个非默认抽象方法接口 的转换 —— 对于符合这个条件的接口(称之为 SAM Type ),在 Kotlin 中可以直接用 Lambda 来表示 —— 当然前提是 Lambda 的所表示函数类型能够跟接口的中方法相匹配。还是拿代码来说事吧!
Java当中有这么一个类,里面存在这一个成员属性和两个方法,这个成员属性是一个ArrayList,两个方法是一个往集合当中放Runnable的子类对象,而另一个移除Runnable子类对象,示例代码如下:
import java.util.ArrayList;
public class RunnableManager {
private ArrayList arrayList = new ArrayList();
public void addRunnable(Runnable runnable){
if(arrayList.add(runnable)) {
System.out.println("添加[" + runnable + "]到集合成功");
}
System.out.println("此时集合当中的总元素个数为:"+arrayList.size());
}
public void removeRunnable(Runnable runnable){
if(arrayList.remove(runnable)){
System.out.println("从集合中移除["+ runnable +"]成功");
}
System.out.println("此时集合当中的总元素个数为:"+arrayList.size());
}
}
在Kotlin中这两个方法是可以接收Lambda表达式的,为什么呢? 因为Lambda表达式可以作为参数传递给Runnable类型的参数,这种Lambda表达式是有限制的,必须是(…) -> Unit类型的才可以,也就是返回类型是Uint的Lambda表达式都可以,看看下面的代码:
fun main(args: Array) {
var runnableManager = RunnableManager()//生成Java中的RunnableManager对象
var runnableLamada = { getString("dsadasda",5)}//Lambda表达式
runnableManager.addRunnable(runnableLamada)//将上面的Lambda表达式作为参数传递给addRunnable()方法
runnableManager.addRunnable(runnableLamada)
runnableManager.addRunnable(runnableLamada)
runnableManager.addRunnable(runnableLamada)
}
fun getString(str:String,i:Int){
println(str+":"+i)
}
输出:
注意笔者的输出结果可能与你不一致,因为实例对象的生成在虚拟机内存当中是随机分配的,无需太通过结果我们可以看出每次调用Lambda表达式的引用runnableLamada都会生成一个Runnable的实现的实例对象,那么移除的时候肯定是存在问题的,比如运行以下代码:
fun main(args: Array) {
var runnableManager = RunnableManager()
var runnableLambda = { getString("dsadasda",5)}
println("添加元素:")
runnableManager.addRunnable(runnableLambda)
runnableManager.addRunnable(runnableLambda)
runnableManager.addRunnable(runnableLambda)
runnableManager.addRunnable(runnableLambda)
println("\n删除元素:")
runnableManager.removeRunnable(runnableLambda)//这里调用runnableLambda会生成一个新的实现的Runnable实例对象,所以肯定是移失败的
runnableManager.removeRunnable(runnableLambda)
runnableManager.removeRunnable(runnableLambda)
runnableManager.removeRunnable(runnableLambda)
}
fun getString(str:String,i:Int){
println(str+":"+i)
}
输出:
由结果可见,元素并没有成功移除掉,为什么呢?我们前面已经说了,每次调用Lambda表达式的引用runnableLamada都会生成一个Runnable的实现的实例对象,所以肯定是移除失败的,如果不信,你在RunnableManager类中修改方法removeRunnable如下所示:
public void removeRunnable(Runnable runnable){
System.out.println("移除的元素:["+runnable+"]");
if(arrayList.remove(runnable)){
System.out.println("从集合中移除["+ runnable +"]成功");
}
System.out.println("此时集合当中的总元素个数为:"+arrayList.size());
}
重新运行Kotlin中的代码输出以下结果:
添加元素:
所以啊!在使用SAM的时候一定要注意一点,就是每次作为参数去调用Java里的方法,这个Lambda表达式都会去新建一个实现接口的实例对象。
在Kotlin中处理和操作正则表达式有两种方式,第一种使用Java当中封装的正则表达式库,我想这笔者不用讲了吧,第二种使用Kotlin自己的正则表达式工具类,那我们来看看Kotlin当中正则表达式的工具类吧:
先来看看Java当中的一段代码:
public static void main(String[] args){
String str = "00A11B22C33D44E55F66G77H88I99";//把这个字符串切割,让它只剩下数字,并且存入一个String[]中
String geShi = "[A-Z]";
String[] results = str.split(geShi);
for(String s:results){
System.out.println(s);
}
}
把这段代码改写成Kotlin的方式,示例代码如下:
fun main(args: Array) {
var str = "00A11B22C33D44E55F66G77H88I99"
var geShi = "[A-Z]"
var results = Regex(geShi).split(str)//Regex为Kotlin中处理正则表达式的工具类
for(s in results){
println(s)
}
}
Kotlin可以毫无烦恼的使用Java当中的集合框架,示例代码如下:
fun main(args: Array) {
var arrayList = ArrayList()//java风格
arrayList.add("1")
arrayList.add("2")
arrayList.add("3")
arrayList.add("4")
arrayList.add("5")
arrayList.remove("1")
arrayList.remove("2")
arrayList.remove("3")
arrayList.remove("4")
for(str in arrayList){
println(str)
}
}
Kotlin中的集合是不可变的,什么意思,意思就是说没有添加和删除方法的实现,目前笔者也不知道为何这样设计,或许之后的Kotlin版本应该会改变Kotlin自身集合的一些特性吧,反正目前为止只需要用好Java集合框架即可,以Map集合为例子:
fun main(args: Array) {
var maps1 = mapOf("a" to 1,"b" to 2,"c" to 3,"c" to 3,"c" to 3,"c" to 3,"c" to 3)
//这么声明是不能直接调用put和remove等基本操作的方法
var maps2 = mapOf(1 to 2,2 to 3,3 to 4,4 to 5, 5 to 6,6 to 7,7 to 8)
//mps2.remove(1) //这么写是错误的,因为没有remove()方法让你去调用
for((key,value) in maps1){
println("Key:${key},Value:${value}")
}
}
所以呢,你只要会使用Java中的集合框架,那么Kotlin中使用集合框架就不是什么问题了。
Kotlin中的IO操作还是使用Java IO操作库,只不过呢,Kotlin中封装了一些便利的扩展方法,没有使用扩展方法的代码如下所示:
fun main(args: Array) {
var br = BufferedReader(FileReader(File("D:\\A.txt")))
var line = ""
while(true){//注意这里不要写成 (line = br.readLine())!=null,因为Kotlin当中语句"line = br.readLine()"不再返回line的值
line = br.readLine()?:break
println(line)
}
br.close()
}
6.1 使用”use”自动关闭读写文件
fun main(args: Array) {
var br = BufferedReader(FileReader(File("D:\\A.txt"))).use {
while(true){
var line = it.readLine()?:break
println(line)
}
}//执行完use里面的Lambda表达式将会自动执行br.close,这就是use的好处
}
6.2 readLines直接获取文件所有内容
File("D:\\A.txt").readLines().forEach(::println)
比如你要在Kotlin中去实现Java的接口Game,代码如下所示:
public interface Game {
void getID(int id);
void getID(Integer id);
}
由于Java的装箱以及拆箱,实现这个Game接口就会出现冲突,这两个getID()方法被实现的时候总是被认为为同一个方法,因为Kotlin中只存在Int类型,所以Kotlin把int id当成了Int id,Integer id也当成了Int id,所以冲突就出现了,你怎么能实现Game接口后,出现两个名字相同,参数类型也相同的方法呢?这就是Java装箱和拆箱带来的麻烦,那么如何解决这中问题呢?解决方式有很多,去修改Game接口是一种,但是怎样在不修改Game接口的情况下解决问题呢?使用Java中间类啊,在Java中让某个中间类去实现这个Game接口,然后在kotlin当中再去继承这个类不就解决问题了吗?示例代码如下:
Java中间类:
public class MyGame implements Game{
@Override
public void getID(int id) {
System.out.println("这是int参数的方法实现");
}
@Override
public void getID(Integer id) {
System.out.println("这是Integer参数的方法实现");
}
}
Kotlin中的代码:
fun main(args: Array) {
var myGame = MyGame_Kotlin()
myGame.getID(3)
}
class MyGame_Kotlin : MyGame()