美文网首页Kotlin
Kotlin基础(3)-类

Kotlin基础(3)-类

作者: 取了个很好听的名字 | 来源:发表于2019-11-01 12:00 被阅读0次

    前言

    本文主要介绍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的相关基础内容。

    相关文章

      网友评论

        本文标题:Kotlin基础(3)-类

        本文链接:https://www.haomeiwen.com/subject/euhfbctx.html