深入kotlin- 伴生对象和扩展
Posted 颐和园
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入kotlin- 伴生对象和扩展相关的知识,希望对你有一定的参考价值。
伴生对象
在 kotlin 中,类没有 static 方法的概念,这与 java 不同。kotlin 用 package 级别的函数来取代静态方法(在字节码层级,这就是静态方法)。
所谓伴生对象其实就是位于 class 中的 object,使用 companion object 关键字声明:
class A
comopanion object: MyObject
var a:Int = 100
fun method()
你可以通过 类名.对象名.方法名
调用:
A.MyObject.method()
甚至你还可以通过 类名.方法名
调用:
A.method()
println(A.a)
这就类似了静态成员的调用方法。在 kotlin 中,我们可以利用伴生对象实现类的静态成员。同时,kotlin 还规定了同一个类中只允许定义一个伴生对象,从而避免了多个伴生对象的命名冲突。因为类中只有一个伴生对象,所以伴生对象的名字是不必要的,完全可以省略:
class A
comopanion object
var a:Int = 100
fun method()
同时 kotlin 提供一个固定的对象名 Companion。通过 javap -c 反编译可以看到,伴生对象本质上是一个静态属性,它的类型是一个内部类,而它的成员则是实例成员。但是,你可以通过 @JvmStatic 注解将它们变成静态成员:
class A
comopanion object
var a:Int = 100
@JvmStatic
fun method()
这样,method() 方法中 jvm 层面上来说是 static 的了,但是这并对使用层面上没有任何影响,调用方式不变。
可见性
Kotlin 中有 4 种访问修饰符。
private
- 如果是顶层声明(包级别),则只能在该文件内可用。
- 如果是类成员,只能在当前类可用
protected
- 不能用于顶层声明(包括函数和类)。
- 如果是类成员,当前类和子类可用。
internal
- 只能在同一模块(project)下可用。
- 如果是类成员,在同一模块下可用
public
- 如果是顶层声明(包级别)不添加任何访问修饰符,就是 public。
- 如果是类成员,任何地方可用。
对于局部变量,没有可见性的概念,不能使用访问修饰符。
扩展
等同于 swift 中的 extension,但形式上不同(更加简化)。
class A
fun A.add(a: Int, b: Int) = a+b
...
let a = A()
let num = a.add(1,2)
以上代码为类 A 动态地扩展了新方法 add。
不支持多态
open class A
class B:A()
fun A.method()="A"
fun B.method()="B"
fun method(obj: A)
println(obj.method())
fun main(args: Array<String>)
method(A()) // 打印 A
method(B()) // 打印 A -> 扩展不支持多态
没错 method(B()) 一句打印的结果证明了,扩展不支持多态,因为扩展的方法是静态的,不是动态的,因此尽管传入了一个 B 对象,但在 obj.method() 方法在编译时就已经决定了它会指向 A 类的 method 扩展方法,而不是 B 类的 method 方法。也就是说,kotlin 编译器在编译时将 obj.method() 一句自动编译成了 A 的扩展方法。而字节码一旦构建就无法改变。所以无论运行时你传入 A 对象还是 B 对象,这个方法只会执行 A 对象的 method 方法。
不支持重写,但可以重载
扩展不支持对原有方法对覆盖。但可以对原有方法进行重载(参数列表不同)。
可空扩展
扩展可以扩展可空类型。通过这种方式,可以简化该类型的判空操作,从而使调用者减少不必要的判空操作。
fun Any?.toString():String
if(null==this)
return "null"
return toString()
注意,这不是重写,不会覆盖原有类的 toString 方法。
扩展属性
val A.name:String
get()="hello"
扩展伴生对象
class A
companion object Obj
fun A.Obj.method() println("hello")
...
A.method() // 打印 hello, 注意,伴生对象的目的就是作为 java static 成员的替代物,因此可以直接通过类名调用
分发接收者/扩展接收者
除了在顶层进行扩展外,也可以在类的内部定义,则这个类被 kotlin 叫做分发接收者(dispatch receiver)。被扩展的类叫做扩展接收者(extension receiver)。当两个接收者出现命名冲突时,后者优先级更高。
class A
fun method()
println("A.method")
class B
fun method2()
fun A.hello() // 这是对 A 的扩展,所以 A 中的所有成员对此方法可见,同时它是定义在 B 中的,所以 B 的所有成员可见
method() // 调用 A 的方法
method2() // 调用 B 的方法
fun world(b: B)
b.hello()
fun A.output():String
return toString()
注意,B 中有一个方法对 A 进行了扩展,所以该扩展可同时访问 A 和 B 的成员。同时,该扩展方法在 B 中可见,在 B 以外不可见。
此外,在 A 的扩展方法 output 中出现了一个命名冲突 toString()。因为 A 和 B 都有 toString 方法。这种情况下,优先调用扩展接收者(即 A)的 toString 方法。如果你想调用 B (分发接收者)的 toString 方法,则可以:
return this@B.toString()
以上是关于深入kotlin- 伴生对象和扩展的主要内容,如果未能解决你的问题,请参考以下文章