前言
本文主要介绍Kotlin中类的创建以及使用
定义类
kotlin中定义类的方式如下:
class A (){
}
class 声明其是一个类,A代表类名,{}代表类体
Java字节码如下:
@Metadata(
mv = {1, 1, 10},
bv = {1, 0, 2},
k = 1,
d1 = {"\u0000\f\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002¨\u0006\u0003"},
d2 = {"Lcom/zhqy/javademo/A;", "", "()V", "production sources for module app"}
)
public final class A {
}
可以看出类默认被final修饰,不可以被修改继承,如果想被其他类继承可以使用open关键字
open class A (){
}
java字节码如下
@Metadata(
mv = {1, 1, 10},
bv = {1, 0, 2},
k = 1,
d1 = {"\u0000\f\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\b\u0016\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002¨\u0006\u0003"},
d2 = {"Lcom/zhqy/javademo/A;", "", "()V", "production sources for module app"}
)
public class A {
}
如果想要限定类的作用范围可以使用可见性修饰符进行限定
可见性修饰符.png
代码如下:
private class A (){
}
Java 字节码
@Metadata(
mv = {1, 1, 10},
bv = {1, 0, 2},
k = 1,
d1 = {"\u0000\f\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\b\u0002\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002¨\u0006\u0003"},
d2 = {"Lcom/zhqy/javademo/A;", "", "()V", "production sources for module app"}
)
final class A {
}
构造方法
Kotlin中有一个主构造的说法,即创建这个类的构造方法,而构造方法的重载成为二级构造器,所有的二级构造器都直接或间接的使用主构造器。主构造器的创建如下:
private class A(var name:String,var age:Int ){
}
在括号中添加需要传递进来的数据来构造类对象。
java字节码如下:
@Metadata(
mv = {1, 1, 10},
bv = {1, 0, 2},
k = 1,
d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\b\n\u0002\b\n\b\u0002\u0018\u00002\u00020\u0001B\u0015\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\u0006\u0010\u0004\u001a\u00020\u0005¢\u0006\u0002\u0010\u0006R\u001a\u0010\u0004\u001a\u00020\u0005X\u0086\u000e¢\u0006\u000e\n\u0000\u001a\u0004\b\u0007\u0010\b\"\u0004\b\t\u0010\nR\u001a\u0010\u0002\u001a\u00020\u0003X\u0086\u000e¢\u0006\u000e\n\u0000\u001a\u0004\b\u000b\u0010\f\"\u0004\b\r\u0010\u000e¨\u0006\u000f"},
d2 = {"Lcom/zhqy/javademo/A;", "", "name", "", "age", "", "(Ljava/lang/String;I)V", "getAge", "()I", "setAge", "(I)V", "getName", "()Ljava/lang/String;", "setName", "(Ljava/lang/String;)V", "production sources for module app"}
)
final class A {
@NotNull
private String name;
private int age;
@NotNull
public final String getName() {
return this.name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.name = var1;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
public A(@NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
this.age = age;
}
}
可以看见类中声明了成员变量和get,set方法,也就是说传递进来的参数会被添加到成员变量中并提供get和set方法。如果不想这样做的话可以去掉var关键字
private class A( name:String, age:Int ){
}
java字节码如下:
@Metadata(
mv = {1, 1, 10},
bv = {1, 0, 2},
k = 1,
d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\b\n\u0002\b\u0002\b\u0002\u0018\u00002\u00020\u0001B\u0015\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\u0006\u0010\u0004\u001a\u00020\u0005¢\u0006\u0002\u0010\u0006¨\u0006\u0007"},
d2 = {"Lcom/zhqy/javademo/A;", "", "name", "", "age", "", "(Ljava/lang/String;I)V", "production sources for module app"}
)
final class A {
public A(@NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
}
}
不过这显然是没有意义的,也不推荐这样做。
那么如何在构造方法中添加代码实现响应功能呢?kotlin提供了init代码块来解决该问题
private class A(var name:String, var age:Int ){
init {
println("我在主构造方法中1");
}
init {
println("我在主构造方法中2");
}
}
java字节码
@Metadata(
mv = {1, 1, 10},
bv = {1, 0, 2},
k = 1,
d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\b\n\u0002\b\n\b\u0002\u0018\u00002\u00020\u0001B\u0015\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\u0006\u0010\u0004\u001a\u00020\u0005¢\u0006\u0002\u0010\u0006R\u001a\u0010\u0004\u001a\u00020\u0005X\u0086\u000e¢\u0006\u000e\n\u0000\u001a\u0004\b\u0007\u0010\b\"\u0004\b\t\u0010\nR\u001a\u0010\u0002\u001a\u00020\u0003X\u0086\u000e¢\u0006\u000e\n\u0000\u001a\u0004\b\u000b\u0010\f\"\u0004\b\r\u0010\u000e¨\u0006\u000f"},
d2 = {"Lcom/zhqy/javademo/A;", "", "name", "", "age", "", "(Ljava/lang/String;I)V", "getAge", "()I", "setAge", "(I)V", "getName", "()Ljava/lang/String;", "setName", "(Ljava/lang/String;)V", "production sources for module app"}
)
final class A {
@NotNull
private String name;
private int age;
@NotNull
public final String getName() {
return this.name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.name = var1;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
public A(@NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
this.age = age;
String var3 = "我在主构造方法中1";
System.out.println(var3);
var3 = "我在主构造方法中2";
System.out.println(var3);
}
}
在Java字节码中添加了两条打印信息,而且打印顺序与声明顺序相同,这就意味着在类中可以构建多个init方法,且执行顺序与声明顺序一致。
二级构造器
当类的构造方法有多个重载时,可以使用constructor来构建该重载方法,kotlin中成为次级构造器或二级构造器,需要注意的是,该构造器需要直接或间接调用主构造器
class A(var name:String, var age:Int ){
constructor ( name:String):this(name,18){
}
}
java字节码
public final class A {
@NotNull
private String name;
private int age;
@NotNull
public final String getName() {
return this.name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.name = var1;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
public A(@NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
this.age = age;
}
public A(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
this(name, 18);
}
}
可以看到二级构造调用了主构造器。那么如果构造器调用类中方法需要注意什么呢?
先看一下这个例子
class A(var name:String, var age:Int ){
init {
foo()
}
fun foo(){
print(num)
}
var num=10;
}
java字节码
public final class A {
private int num;
@NotNull
private String name;
private int age;
public final void foo() {
int var1 = this.num;
System.out.print(var1);
}
public final int getNum() {
return this.num;
}
public final void setNum(int var1) {
this.num = var1;
}
@NotNull
public final String getName() {
return this.name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.name = var1;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
public A(@NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
this.age = age;
this.foo();
this.num = 10;
}
}
从字节码中可以看出当调用foo方法时num成员变量还没有赋值,执行该方法会打印0,也就是说在执行init代码块时代码块中的数据都应当在调用之前进行赋值。
11-01 11:18:40.837 13672-13672/? I/System.out: 0
那么二级构造器有这种情况吗?
代码
class A(var name:String, var age:Int ){
constructor(name:String):this(name,18){
foo();
}
fun foo(){
println(num);
}
var num=10;
}
测试结果
11-01 11:25:04.038 14453-14453/? I/System.out: 10
通过测试未发现二级构造器会出现这种情况.
成员变量的声明
类中成员变量的声明可以这样操作
open class A(var name:String, var age:Int ){
var num:String="成员变量";
}
这样就声明了一个成员变量,也可以用可见性修饰符来修饰该成员变量。
那么如何使用该成员变量呢?其实与java差不多,可以通过getxxx和setXXX来获取和设置数据
open class A(var name:String, var age:Int ){
var num:String="成员变量"
//创建setter方法
set(value) {
field=value;
}
//c创建getter方法
get() {
return field;
}
}
var a=A("zhangsan",18);
//调用set方法
a.name="张三"
//这里调用的是get方法
println(a.age);
println(a.name);
测试结果:
11-01 11:49:00.693 15596-15596/com.zhqy.javademo I/System.out: 18
11-01 11:49:00.693 15596-15596/com.zhqy.javademo I/System.out: 张三
需要注意的是这里的a.name=“张三”在底层实现调用的A中的setName(String name)来注入数据,a.name在底层使用的是getName()来获取数据。也就是说A(this).xxx实际调用的xxx的get或set方法
类的继承
如果一个类想要继承父类可以这样操作:
open class A(var name:String, var age:Int ){
init {
println("A的构造方法");
}
open fun foo(){
println("调用 A中的foo 方法");
}
}
class B(var name1:String,var age1:Int ):A(name1,age1 ){
init {
println("B 的构造方法");
}
override fun foo() {
super.foo()
println("调用 B中的方法");
}
}
测试结果
11-01 11:34:50.445 14939-14939/com.zhqy.javademo I/System.out: A的构造方法
11-01 11:34:50.445 14939-14939/com.zhqy.javademo I/System.out: B 的构造方法
11-01 11:34:50.446 14939-14939/com.zhqy.javademo I/System.out: 调用 A中的foo 方法
11-01 11:34:50.446 14939-14939/com.zhqy.javademo I/System.out: 调用 B中的方法
如果子类想要继承父类则需要使用:来声明继承的父类,且父类需要声明关键字open (类默认被public final 修饰,无法被继承或修改),当父类有主构造器时子类需要调用父类的主构造器,父类中的方法需要被关键字open修饰才能被子类重写,并通过super.xxx()来调用父类方法
如果子类中的内部类想要调用父类中的方法该如何操作呢?
open class A(var name:String, var age:Int ){
fun foo(){
println("A 中的foo方法")
}
}
class B(var name1:String ,var age1:Int):A(name1,age1){
inner class C(){
fun foo1(){
super@B.foo();
}
}
}
var a=B("zhangsan",18);
//调用A中的foo方法
a.C().foo1()
代码中B首先继承自A,B中有一个用inner关键字修饰的内部类C,如果C想要调用A中的方法需要使用kotlin的一个语法: super@XXX.xxx();即调用XXX的父类的xxx方法。测试结果如下
11-01 11:59:31.298 16329-16329/com.zhqy.javademo I/System.out: A 中的foo方法
以上就是Kotlin中class的相关基础内容。
网友评论