Kotlin上手(一)

45次阅读

共计 5772 个字符,预计需要花费 15 分钟才能阅读完成。

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

正文完
 0