Kotlin上手(一)
系列笔记为学习极客时间张涛讲解Kotlin的笔记。
本篇笔记主要学习了从Java过渡到Kotlin的几个注意点
1.最基本语法:
1.1Kotlin的变量:
Kotlin的文件是以.kt结尾,Kotlin的代码不需要;结尾
var表示变量,val表示不可变的变量(不是常量),Kotlin的变量名在前面,变量名写在后面,中间冒号隔开,如果变量类型编译器可以推断出来,那么可以不用写明类型,同时Kotlin是具有空安全的,通过?和!!可以实现这种空安全的转换。
例如:
val name1:String = null //错误,String类型不为空,不能赋值null
val 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.kt
fun pln(str:String){
println("$str")
}
//Main.java
public static void main(args[] String){
UtilKt.echo("$args[0]");
}
其中$是Kotlin语法的转义符号之一,可以直接在字符串中插入变量名或者一段代码(用{}括起来),如果要在字符串中使用$符号,则可以
println("${'$'}name") //output:$name
2.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 = 127
val num2:Int? = num1
val num3:Int? = num1
fun 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 num1
public final staticMethod()V
//加了注解
public static I num1
public 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
修饰.
@JVMField
class Key(val value: Int) {
companion object {
@JvmField
val COMPARATOR: Comparator<Key> = compareBy<Key> { it.value }
}
}
// Java
Key.COMPARATOR.compare(key1, key2);
// public static final field in Key class
lateinit
object Singleton {
lateinit var provider: Provider
}
// Java
Singleton.provider = new Provider();
// public static non-final field in Singleton class
const
// file example.kt
object Obj {
const val CONST = 1
}
class C {
companion object {
const val VERSION = 9
}
}
const val MAX = 239
int 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 fine
C.bar(); // error: not a static method
C.Companion.foo(); // instance method remains
C.Companion.bar(); // the only way it works
注意Companion,如果是直接object修饰类,则是INSTANCE,如下:
object Obj {
@JvmStatic fun foo() {}
fun bar() {}
}
Obj.foo(); // works fine
Obj.bar(); // error
Obj.INSTANCE.bar(); // works, a call through the singleton instance
Obj.INSTANCE.foo(); // works too
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。