Kotlin学习历程——类的定义

Kotlin语言中文站

定义类

Kotlin中使用关键字class声明一个类,类声明由类名、类头(指定其类型参数、主构造函数等)以及由花括号包围的类体构成。 类头、类体是可选的,如果一个类没有类体,可以省略花括号

//声明Person类
class Person { /* .... */ }

//没有类体,可以省略花括号
class Person

构造函数

在kotlin中一个类可以有一个或者没有主构造函数以及一个或多个次构造函数。主构造函数是类头的一部分,它跟在类名(与可选的类型参数)后面。

//携带主构造函数的Person类
class Person constructor(firstname: String) { /* ... */ }

//如果主构造函数没有任何注解或者可见性修饰符,可以省略 constructor 关键字
class Person(firstname: String) { /* ... */ }

主构造函数不能包含任何的代码,那如果我想增加一些初始化代码怎么办? kotlin中的初始化块 init 应运而生。

初始化块(initializer blocks)

class Person(firstname: String) {
    //主构造器的参数firstname也可以在类体内声明的属性初始化器中使用
    val propertyOne = "propertyOne##firstname:$firstname.".also { println(it) }

    //初始化块1
    init {
        //这里可以使用主构造器的参数firstname
        println("init1##firstname:$firstname.")
    }

    //主构造器的参数firstname也可以在类体内声明的属性初始化器中使用
    val propertyTwo = "propertyTwo##firstname:$firstname.".also { println(it) }
    
    //初始化块2
    init {
        //这里可以使用主构造器的参数firstname
        println("init2##firstname:$firstname.") 
    }
}

//执行上面的代码
fun main(args: Array<String>) {
    Person("hepingdev")
}


//打印结果
propertyOne##firstname:hepingdev.
init1##firstname:hepingdev.
propertyTwo##firstname:hepingdev.
init2##firstname:hepingdev.

如上,一个类可以有一个或多个初始化块,初始化块按照它们出现在类体中的顺序执行,与属性初始化器优先级一样。 这里我们不妨把上面的代码转成Java代码,看看init到底是个啥?

public final class Person {
   @NotNull
   private final String propertyOne;
   @NotNull
   private final String propertyTwo;

   @NotNull
   public final String getPropertyOne() {
      return this.propertyOne;
   }

   @NotNull
   public final String getPropertyTwo() {
      return this.propertyTwo;
   }

   //主构造函数
   public Person(@NotNull String firstname) {
      super();
      String var2 = "propertyOne##firstname:" + firstname + '.';
      System.out.println(var2);      ----> 打印属性1
      this.propertyOne = var2;
      var2 = "init1##firstname:" + firstname + '.';
      var3 = false;
      System.out.println(var2);     ----> 打印初始化块1
      var2 = "propertyTwo##firstname:" + firstname + '.';
      System.out.println(var2);       ----> 打印属性2
      this.propertyTwo = var2;
      var2 = "init2##firstname:" + firstname + '.';
      System.out.println(var2);     ----> 打印初始化块2
   }
}

由上可知,它的作用就是在主构造函数里面增加操作性代码即初始化块中的代码实际上会成为主构造函数的一部分

次构造函数

类中声明前缀有constructor 的次构造函数。

class Person {
    //次构造函数
    constructor(secondname:String, modifydata:String) {
        println("secondname:$secondname modifydata:$modifydata")
    }
}

次构造函数可以简单理解:解决kotlin中只能定义一个主构造函数的缺陷,毕竟开发中类会存在多个构造器的需求。

如果类有主构造函数,每个次构造函数必须委托给主构造函数(否则会编译报错),可直接委托或者通过别的次构造函数间接委托,通过关键字this进行委托。

class Person(name:String) {

    init {
        println("init####.")
    }

    constructor(name:String, age: Int) : this(name, age, "") {
        println("constructor##间接委托")
    }

    constructor(name: String, age: Int, address: String) : this(name) {
        println("constructor##直接委托")
    }
}

没有主构造函数,虽然不用显式声明委托,但实际仍会隐式发生委托,从如下代码打印结果可看出:

class Person {
    init {
        println("init####.")
    }

    constructor(name:String, age: Int) {
        println("constructor##2")
    }

    constructor(name: String, age: Int, address: String) {
        println("constructor##3")
    }
}


fun main(args: Array<String>) {
    Person("hepingdev", 100, "中国")//打印什么?
    //打印结果
    init####.
    constructor##3
    
    
    Person("hepingdev", 100)//打印什么?
    //打印结果
    init####.
    constructor##2
}

上面说到,初始化块实际上会成为主构造函数的一部分,委托给主构造函数会作为次构造函数的第一条语句,因此所有初始化块与属性初始化器中的代码都会在次构造函数之前执行。即使类没有主构造函数,这种委托仍会隐式发生,并且仍会执行初始化块

如果一个非抽象类没有声明任何(主/次)构造函数,那么会自动生成一个不带参数的主构造函数

//kotlin类
class Person

//java代码如下
public final class Person {
    //一般默认不写
    public Person(){} 
}

声明属性

类中声明属性kotlin有个简洁的写法:

class Person(var name:String, var age: Int, var address: String) { ... }

上面是在主构造函数声明,当然也可以在类体中声明:

class Person {
    var name:String = ""
    var age: Int = 0
    var address:String=""
}

转成Java代码如下所示:

/**  主构造函数声明 */
public final class Person {
   @NotNull
   private String name;
   private int age;
   @NotNull
   private String address;

   @NotNull
   public final String getName() {
      return this.name;
   }

   public final void setName(@NotNull String var1) {
      this.name = var1;
   }

   public final int getAge() {
      return this.age;
   }

   public final void setAge(int var1) {
      this.age = var1;
   }

   @NotNull
   public final String getAddress() {
      return this.address;
   }

   public final void setAddress(@NotNull String var1) {
      this.address = var1;
   }

   public Person(@NotNull String name, int age, @NotNull String address) {
      this.name = name;
      this.age = age;
      this.address = address;
   }
}


/**   类体中声明    */
public final class Person {
   @NotNull
   private String name = "";
   private int age;
   @NotNull
   private String address = "";

   @NotNull
   public final String getName() {
      return this.name;
   }

   public final void setName(@NotNull String var1) {
      this.name = var1;
   }

   public final int getAge() {
      return this.age;
   }

   public final void setAge(int var1) {
      this.age = var1;
   }

   @NotNull
   public final String getAddress() {
      return this.address;
   }

   public final void setAddress(@NotNull String var1) {
      this.address = var1;
   }
}

两种写法没啥区别。

创建类的实例

注意 Kotlin 没有 new关键字。

//无参构造函数
val p = Person()

//携带参数的构造函数
val p = Person("hepingdev", 100, "中国")

次构造函数,初始化块,属性初始化器执行顺序?

如下代码, 打印什么?

class Person(name: String, age: Int) {

    var name: String = "i am $name, $age years old.".also { println(it) }

    init {
        println("init####.")
    }

    constructor(name: String, age: Int, address: String):this(name, age) {
        println("constructor##i am $name, $age years old, from $address")
    }
}


fun main(args: Array<String>) {
    //调用主构造函数
    Person("hepingdev", 100) //打印什么
    //打印如下
    //i am hepingdev, 100 years old.
    //init####.
    
    
    //调用次构造函数
    Person("hepingdev", 100, "中国")//打印什么
    //打印如下
    //i am hepingdev, 100 years old.
    //init####.
    //constructor##i am hepingdev, 100 years old, from 中国
}

根据打印结果,结论如下:

  • 调用主构造函数初始化: super() -> init() / 属性初始化器
  • 调用次构造函数初始化: super() -> init() / 属性初始化器 -> 次构造函数

下一篇:Kotlin学习历程——继承。

你可能感兴趣的:(#,Kotlin,kotlin)