Kotlin上手(一)系列笔记为学习极客时间张涛讲解Kotlin的笔记。本篇笔记主要学习了从Java过渡到Kotlin的几个注意点1.最基本语法:1.1Kotlin的变量:Kotlin的文件是以.kt结尾,Kotlin的代码不需要;结尾var表示变量,val表示不可变的变量(不是常量),Kotlin的变量名在前面,变量名写在后面,中间冒号隔开,如果变量类型编译器可以推断出来,那么可以不用写明类型,同时Kotlin是具有空安全的,通过?和!!可以实现这种空安全的转换。例如:val name1:String = null //错误,String类型不为空,不能赋值nullval name1:String? = null //正确:String?类型可以为空(String与String?是两种不同类型)val name2:String = name1 //错误:name2是String类型不可以为空,而name1可能为空,不能赋值val name2:String = name1!! //正确:后面的!!指明开发者已经确保了name1不可能是null,可以赋值(如果运行时出现null,抛出相关的异常)1.2kotlin的函数:fun关键字指明Kotlin的函数变量,后面跟函数名,参数列表同变量写法一样,先写变量名后写变量类型,返回值最后写。fun functionName(str:String,num:Int):String{ if(num==0){ println(str) } return str}2.Kotlin与Java的互相调用:2.1.Java直接调用Kt函数kotlin的函数可以直接写在文件里面,不用写在类里面,但是编译kt文件之后,实际上最终还是编译成public static修饰//Util.ktfun pln(str:String){ println("$str")}//Main.javapublic static void main(args[] String){ UtilKt.echo("$args[0]");}其中$是Kotlin语法的转义符号之一,可以直接在字符串中插入变量名或者一段代码(用{}括起来),如果要在字符串中使用$符号,则可以println("${’$’}name") //output:$name2.2.匿名类对象:匿名内部类主要是针对那些不能创建实例的抽象类和接口而来的,在Kotlin中使用object关键字创建匿名类对象:首先看下Java和Kotlin的匿名类对象写法上的区别://在Java中创建和使用匿名内部类public interface AInterface { void sayMsg(String msg); void doMsg(String msg);}AInterface aInterface = new AInterface() { @Override public void sayMsg(String msg) { System.out.println(“sayMsg”+msg); } @Override public void doMsg(String msg) { System.out.println(“doMsg”+msg); }};aInterface.sayMsg(“B”);aInterface.doMsg(“B”);//在Kotlin中创建和使用匿名内部类interface KtInterface{ fun sayBye(msg:String?) fun doBye(msg:String?)}val bye = object: KtInterface{ override fun doBye(msg: String?) { println(“doBye$msg”) } override fun sayBye(msg: String?) { println(“sayBye$msg”) }}bye.sayBye(" bye")bye.doBye(" Bye")如果要在Java中调用Kotlin中的匿名类对象,则如下:类名加.INSTANCE而Kotlin的单例写法之一,也正是直接用object关键字实现。//在Test.kt文件中object Test{ fun sayMessage(msg:String?){ println(msg) }}//在MainTest.java文件中public static void main(String[] args){ Test.INSTANCE.sayMessage(“Hello”);}如果不通过INSTANCE来能不能调用到sayMessage方法?直接通过Test调用Test.sayMessage(“Hello”)可以通过@JVMStatic注解实现,该注解最终会把对应方法在编译成public static修饰。2.3.传递Class对象:在Java中传递一个Class对象的方法是直接ClassName.class而Kotlin如果要传递一个Java的class对象则是ClassName::class.java因为Kotlin的Class对象与Java的Class对象并不相同,Kt有着自己的Class类型:KClass,如果使用的是KClass那么直接ClassName:kotlin示例代码如下:fun printClass(clazz:Class<MainTest>){ println(“Class Name:"+clazz.simpleName);}fun printClass(clazz:KClass<Test>){ println(clazz.simpleName)}//调用printClass(MainTest::class.java)printClass(Test::class)2.4.关键字上的冲突:如果在Java代码中定义的变量或常量名使用到了Kotlin的关键字,需要借助一对单引号’‘解决这个冲突,如下://关键字冲突public static String object = “object”;//Kotlin中调用println(MainTest.object
);3.新手易踩坑3.1.Kotlin没有封装类Kotlin中对于基本数据类型是没有封装类这一说的,这一点不同于Java的int->Interger,float->Float……首先看如下代码://用Java定义了一个接口,两个同名方法,参数类型一个是基本数据类型,另一个是它的封装类public interface NoBoxInterface { void printNum(int num); void printNum(Integer num);}//用Kotlin去实现它:class NoBoxImpl:NoBoxInterface{ override fun printNum(num: Int) {}}上面的代码中,我们只会要也只能实现一个方法,因为Kotlin中也不存在Integer这个类型。扩展一下:碰巧是在搜Kotlin有没有封箱这一说的时候看到的,来看下面这个例子:简单一点说,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。这是在Java1.5中引入的特性,目的是为了节省内存和提高虚拟机对整型数据的处理能力。eg://自动装箱Integer total = 99;//自定拆箱int totalprim = total;val num1:Int = 127val num2:Int? = num1val num3:Int? = num1fun main(args:Array<String>){ println(num2== num3) println(num2===num3) //kotlin中===比较的是地址,换言之,比较的是它俩是不是同一个对象}//output:true true当num1>127的时候//output:true false可能会有点疑惑为什么举这个例子,跟装箱有什么关系,为什么大于与小于等于127输出的结果不一样?打开Idea的Tool->Kotlin->Show Kotlin ByteCode可以查看对应的字节码文件,然后decomplie可以反编译成java代码Int反编译之后对应到int,Int?反编译之后对应到Integer。在用Integer装箱的时候,我们用到了它的valueOf方法Integer i = Integer.valueOf(8);去看下valueOf就能知道为和如此了:public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i);}//low=-128 high=127正是这里,当整型值在-128到127之间的时候,Integer是不会去创建新对象的,而是从IntegerCache中读取,这个类是Integer的内部静态类,存放了-128到127之间的全部Integer对象。回到最初的例子,正是因为上面这一点,当数值在这个限定范围里面的时候,num3和num2指向的都是同一个Integer对象,当超过这个范围,就会去创建新的Integer对象,使得===的结果为false。3.2.Kotlin类型空值敏感这一点在笔记开头已经讲过了//java代码String format(String str) { return str.isEmpty() ? null : str;}//在kotlin中调用fun function(str: String) { val fmt1 = format(str) val fmt2: String = format(str) val fmt3: String? = format(str)}fmt2这一句会抛出NullPointException,而fmt3不会。3.3.Kotlin没有静态变量与静态方法网上很多博客说直接用object关键字修饰的类,它的方法就是static方法;或者companion object{}修饰的就是静态成员,个人认为这种理解是错误的。来看下面一段代码:class StaticTest { var num0 = 1 fun method(){ println("$num0”) } companion object { //注意:@JvmField var num1 = 1 //注意:@JvmStatic fun staticMethod(){ println("$num1") } }}在上面的代码中,我先注释了两个个注解,然后又恢复它们,通过查看编译好的字节码文件://没加注解private static I num1public final staticMethod()V//加了注解public static I num1public final static staticMethod()V可以看出,对于位于object或者companion object{}修饰的域里面的变量而言 ,kotlin的确把它们看做了static域,但是方法却不一样,只有在加了@JVMStatic注解之后(注意:这个注解只能在有object修饰的域里面才能使用),staticMethod才被编译成了static属性的方法。那么加与不加有什么区别?毕竟你都可以直接通过ClassName.MethodName来调用它,我个人觉得这更像是一种前面说到过的,Kotlin单例的写法。如果要给一个变量或者一个方法附上“Static”属性(打引号是指明在Kotlin中没有Static这一说,只是能够达到与Java的Static关键字相同的效果),可以通过以下方式来实现:下面我援引Kotlin doc来说明如何在Kotlin中达到Static效果3.3.1.Static字段@JvmField 注解;lateinit 修饰;const 修饰.@JVMFieldclass Key(val value: Int) { companion object { @JvmField val COMPARATOR: Comparator<Key> = compareBy<Key> { it.value } }}// JavaKey.COMPARATOR.compare(key1, key2);// public static final field in Key classlateinitobject Singleton { lateinit var provider: Provider}// JavaSingleton.provider = new Provider();// public static non-final field in Singleton classconst// file example.ktobject Obj { const val CONST = 1}class C { companion object { const val VERSION = 9 }}const val MAX = 239int c = Obj.CONST;int d = ExampleKt.MAX;int v = C.VERSION;3.3.2.Static方法@JVMStatic 注解class C { companion object { @JvmStatic fun foo() {} fun bar() {} }}//在Java中调用C.foo(); // works fineC.bar(); // error: not a static methodC.Companion.foo(); // instance method remainsC.Companion.bar(); // the only way it works注意Companion,如果是直接object修饰类,则是INSTANCE,如下:object Obj { @JvmStatic fun foo() {} fun bar() {}}Obj.foo(); // works fineObj.bar(); // errorObj.INSTANCE.bar(); // works, a call through the singleton instanceObj.INSTANCE.foo(); // works too