kotlin 为我们提供了一种混用冲突的解决方案,当继承的类和接口拥有相同签名方法时子类必须重写父类相同签名方法,否则无法编译通过,此外需要显式通过<>
语法指定使用哪个基类的方法。案例如下:
interface Nb {
fun test() {
println(“nb”)
}
}
open class Tt {
open fun test() {
println(“tt”)
}
}
class ChildNb: Nb, Tt() {
//当继承的类和接口拥有相同签名方法时子类必须重写父类相同签名方法,否则无法编译通过
//此外需要显式通过<>指定使用哪个基类的方法
override fun test() {
super.test()
println(“test”)
super.test()
}
}
/**
运行输出值为:
tt
test
nb
*/
fun testRun() {
var oo = ChildNb()
oo.test()
}
对于抽象类的声明与继承基本和 java 没啥区别,具体案例如下:
open class Base {
open fun method() {
println(“base”)
}
}
abstract class Child: Base() {
//父类的实现方法可以被抽象子类重写成抽象方法供当前抽象类的子类实现
override abstract fun method()
}
class Child1: Child() {
override fun method() {
println(“child1”)
}
}
对象声明及伴生对象
kotlin 的对象声明你可以理解成一种单例的语言层面支持实现能力。定义一个类用 class,声明一个对象用 object,全局声明的对象就是一个对象实例,且全局唯一。案例如下:
//定义了一个名为 TestObject 的对象实例
object TestObject {
fun method() {
println(“666”)
}
}
/**
运行输出值为:
666
*/
fun testRun() {
TestObject.method()
}
kotlin 的伴生对象相对于 java 来说也是一种新的特性,本质来说,kotlin 与 java 不同的是 kotlin 的类没有 static 方法。在大多数情况下 kotlin 推荐的做法是使用包级别的函数来充当静态方法角色,kotlin 会将包级别的函数当作静态方法看待。而 kotlin 中一个类最多只能有一个伴生对象,这个伴生对象也类似 java 的 static 成员。案例如下:
//【工匠若水 加微信 yanbo373131686 联系我,关注微信公众号:码农每日一题 未经允许严禁转载 https://blog.csdn.net/yanbober】
class Yan {
//TestObj 名字可以省略,编译器默认名字是 Companion
//一个类中最多只能有一个伴生对象
companion object TestObj {
var name: String = “666”
fun method() = println("${this.name} – method")
}
}
/**
运行输出值为:
666 – method
777 – method
class cn.yan.test.Yan$TestObj
*/
fun testRun() {
Yan.TestObj.method()
//简写,kotlin 语法糖,没有 @jvmStatic 情况下本质还是转为了 Yan.TestObj 静态成员调用方式
Yan.name = “777”
Yan.method()
println(Yan.TestObj.javaClass)
}
虽然伴生对象的成员看起来像是 java 的静态成员,但在运行期他们依旧是真实对象的实例成员。在 jvm 实现上可以将伴生对象的成员真正生成为类的静态方法与属性,具体通过@JvmStatic
注解实现。
伴生对象的本质原理是在编译后生成一个静态内部类来实现的,我们对上面的案例代码进行反编译(javap)结果如下:
//反编译的 cn.yan.test.Yan 类
yandeMacBook-Pro:test yan$ javap -c Yan.class
Compiled from “Test2.kt”
public final class cn.yan.test.Yan {
//Yan 这个 kotlin 类中的伴生对象名生成了一个静态成员变量,名字为 TestObj
public static final cn.yan.test.Yan$TestObj TestObj;
//Yan 这个 kotlin 类的构造函数
public cn.yan.test.Yan();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."")V
4: return
//Yan 这个 kotlin 类的 static 代码块实例化了Yan$TestObj静态内部类并赋值给当前类的静态成员属性TestObj
static {};
Code:
0: new #38 // class cn/yan/test/Yan$TestObj
3: dup
4: aconst_null
5: invokespecial #41 // Method cn/yan/test/Yan$TestObj."":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
8: putstatic #43 // Field TestObj:Lcn/yan/test/Yan$TestObj;
11: ldc #45 // String 666
13: putstatic #20 // Field name:Ljava/lang/String;
16: return
//在 Yan 这个类中新增了伴生对象里面属性 var name 的 get 操作
public static final java.lang.String access g e t N a m e getName getNamecp();
Code:
0: getstatic #20 // Field name:Ljava/lang/String;
3: areturn
//在 Yan 这个类中新增了伴生对象里面属性 var name 的 set 操作
public static final void access s e t N a m e setName setNamecp(java.lang.String);
Code:
0: aload_0
1: putstatic #20 // Field name:Ljava/lang/String;
4: return
}
如下是 Yan 这个 kotlin 类中伴生对象生成的静态内部类反编译代码:
//反编译的 cn.yan.test.Yan 类中伴生对象生成的静态内部类 Yan$TestObj
yandeMacBook-Pro:test yan$ javap -c Yan$TestObj.class
Compiled from “Test2.kt”
public final class cn.yan.test.Yan$TestObj {
//伴生对象内部属性 var name 的 get 方法
public final java.lang.String getName();
Code:
0: invokestatic #12 // Method cn/yan/test/Yan.access g e t N a m e getName getNamecp:()Ljava/lang/String;
3: areturn
//伴生对象内部属性 var name 的 set 方法
public final void setName(java.lang.String);
Code:
0: aload_1
1: ldc #18 // String
3: invokestatic #24 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
6: aload_1
7: invokestatic #27 // Method cn/yan/test/Yan.access