java转战kotlin:语法差异

刚刚从java转kotlin难免会遇到一些不适应的语法点,在这里做一下记录。本文写于kotlin版本1.2.50。

访问作用域

kotlin有4种访问作用域:

  1. public 定义在任何区域可见。
  2. internal 定义在同包名下可见。
  3. protected 定义在子类可见,其余可见性等同private。模块中的顶级声明不可用这个关键字。
  4. private 定义在同文件可见。

乍看之下和java差不多,只不过friendly换成internal。需要注意的是:

  1. kotlin中不写访问作用域默认public,而java是friendly(interface除外)。
  2. 外部类不能访问到内部类标识为private的成员。

当成员扩展遇到多态

kotlin中可以为一个已经定义的类扩展方法和属性,而无需任何包装类或继承。但是当扩展遇到多态,则多态的行为与直觉不一致:

open class C

class D: C()

fun C.print() {
    print("C")
}

fun D.print() {
    print("D")
}

fun printC(c: C) {
    c.print()
}

fun printD(d: D) {
    d.print()
}

printC(D())     //打印"C"
printD(D())     //打印"D"

还是上面这个例子,看看在泛型集合中的表现:

val list = arrayListOf(C(), D())
for (c: C in list) {
    c.print()
}

//打印:
//c
//c

当父类和子类都定义了同名的扩展函数的时候,print()的行为取决于上下文环境,c的定义类型,在泛型例子中是C类。从printD()可以看出,如果声明类型为D,则print()行为才会与D一致。

另外,扩展(extend)总会让人想到js,但是不同的是,kotlin中通过扩展无法覆盖该类同签名(方法名和参数需求相同)的成员方法;kotlin中扩展的设计动机是为了代替java中的工具类,使用场景上和js还是有很大区别的。

泛型

在java中,泛型参数的边界由super和extend表示。在kotlin中,用变异标识in和out表示,其基本功能和C#一致:

//in承诺Foo的方法只接受T而不返回T类型
class Foo<in T> {
   fun <T> doo(t: T) {}
}

//out承诺Boo的方法只返回T类型而不接受T类型
class BooT> {
   fun <T> next(): T? {
       return null
   }
}

fun doFoo(f: Foo) {
   //由于Foo的方法不返回T类型,任何Foo的操作对于Foo是安全的
   val f2: Foo = f
}

fun doBoo(b: Boo) {
   //由于Boo的方法不接受T作为参数,任何Boo的操作对于Boo是安全的
   val b2: Boo = b
}

可以看出,在基本功用上,in可以类比super,而out类比extend,super/extend试图通过约束泛型的边界来达成类型安全,而in/out试图约束对泛型的操作来达成类型安全,而上面例子与java明显区别的是,java中Boo不能赋值给Boo,因为两者不存在继承关系,而kotlin中却可以。

注意:尽管如此,基于JVM的kotlin仍然存在类型擦除的问题,详见文档。

获取泛型参数的具体类型

在java中,获取泛型参数的类型需要写一个工具类,而通常网上八九成的所谓的获取类型的代码,都有这样那样的问题,一旦开发中有这种需求,可能需要借鉴现成的类库,例如 Gson库中$Gson$Types.canonicalize()的写法。而ktolin中,可以这么写:

inline fun  Activity.start() {
    startActivity(Intent(this, T::class.java))
}

注意由于reified关键字,T可以直接通过.class获取类型。目前版本,reified关键字需要配合inline使用。
更多细节参考官方笔记。

嵌套类与内部类

按照官方例子,标识为inner的嵌套类才是内部类,可以访问外部类的变量。

class Outer {
    private val bar: Int = 1
    inner class Inner {
        fun foo() = bar
    }
}

val demo = Outer().Inner().foo() // == 1

java中的匿名内部类被object表达式代替:

textView.setOnClickListener(object: View.OnClickListener {
    override fun onClick(p0: View?) {
        //...
    }

})

当然,如果未实现的接口方法是唯一的,可以写成lambda表达式,这点和java一样:

textView.setOnClickListener {
     //...
 }

单例

在java中深入探讨一个可靠的、线程安全的、开销小的单例其实需要花一定篇幅,而在kotlin中,可以通过object关键字定义单例:

object Singleton {
    fun doo() {}
}

然后这样调用方法就行了:

Singleton.doo()

Singleton单例会被自动初始化,并且这个过程是线程安全的。

你可能感兴趣的:(学习笔记)