面向对象:类和构造函数

Kotlin 做为类 Java 语言,在面向对象上具备与 Java 类似的特性,可是针对不一样的状况进行了不一样的优化,今天咱们简单介绍一下 Kotlin 中的类和构造函数。java

1. 定义类和建立类的实例

Kotlin 中定义类与 Java 相同,使用 class 关键字:app

class 类名[主构造函数][{
  //类成员
}]

Kotlin 中的类名与 Java 相同,采用骆峰式命名法,首字母大写。ide

不一样的是,Kotlin 中的类若是是空类,没有任何语句,则能够省略大括号(闲的)。函数

要建立一个类的实例,只须要调用类的构造函数,不使用 new 关键字优化

val s = StringBuilder("Hello World")val list: List<String> = ArrayList()


2. 主构造函数

Kotlin 类的构造函数与 Java 有较大区别。首先,Kotlin 把构造函数分为 主构造函数 和 次构造函数,主构造函数写在类头中,有且只有一个;次构造函数写在类语句中,能够有多个,也能够没有。ui

首先,咱们看一个简单的 Java 类:this

public class Person {
    String name;
    public Person(String name) {
        this.name = name;
    }}

这个类定义一个 String 类型的成员变量 name,而后定义了带有一个 String 类型参数的构造函数,这个构造函数把参数列表中的 name 赋给了成员变量 name。spa

用 Kotlin,咱们能够这样写:orm

class Person constructor(name: String) {
    val name: String
    init {
        this.name = name
    }}

首先,咱们使用 class 定义了一个类 Person,而后在类头用 constructor 关键字定义带有一个 String 类型参数的主构造函数。在类体里,咱们定义了一个不可变的 String 类型成员变量 name,而后使用 init 关键字定义主构造函数的行为,把主构造函数的 name 参数赋给成员变量 name。对象

能看到,Kotlin 类的主构造函数,参数列表定义在类声明的部分,函数体却在 init 代码块里。(莫名其妙)

这样写不够简洁呀!实际上,充分利用 Kotlin 提供的特性,咱们能够把这个类缩短到一行:

class Person(val name: String)

看吧,全部冗余信息都已除去,只保留了最关键的部分。想理解这句话,咱们须要知道主构造函数定义中的两个细节:

  1. 若是主构造函数没有任何修饰符,则能够去掉 constructor 关键字。这样,咱们的类定义就很像一个函数了:

    class Person(name: String) {/*……*/}

    若是想使用在主构造函数前使用修饰符,那么这个 constructor 就不能省了:

    class Person private constructor() {/*……*/}
  2. 若是主构造函数中定义的参数使用 val 或者 var 修饰,则会建立与这个参数同名的成员变量,并使用传入的参数值初始化这个成员变量。简单来讲,就是把“定义成员变量”和“使用构造函数传入的参数初始化成员变量”简化为一个 val 或者 var 关键字。

    上面的例子中,咱们在主构造方法里声明 val name: String,Kotlin 就会自动为咱们添加一个名为 name 的不可变的 String 类型成员变量,而后用主构造函数传入的值初始化这个成员变量,能够直接调用。

3. 次构造函数

Java 中,一个类每每有多个不一样的构造函数,它们通常有下面两种关系:

  1. 参数列表由少到多,参数列表少的构造函数使用 默认值 调用参数列表多的构造函数。对于这个常见的类型,Kotlin 中使用 函数默认参数 的方法简化代码;

  2. 不一样的构造方法使用不一样的参数列表,相互之间存在调用关系。Kotlin 中使用 次构造函数委托 的解决方法。

首先看第一种状况,这里咱们给用 Java 写的 Person 类添加一些东西:

public class Person {
  long id;
  String name = "";
  int age = 0;

  public Person(long id, String name, int age) {
    this.id = id;
    this.name = name;
    this.age = age;
  }
  public Person(long id, String name) {
    this.id = id;
    this.name = name;
  }
  public Person(long id, int age) {
    this.id = id;
    this.age = age;
  }
  public Person(long id) {
    this.id = id;
  }}

咱们添加了两个属性,一个 long 类型的 id,一个 int 类型的 age。三个构造函数都须要传入 id 变量,name 和 age 变量的要求则是 若是没有外部传入的参数,就使用默认值

使用 Kotlin,咱们能够大大简化这一长串代码:

class Person(val id: Long, val name: String = "", val age: Int = 0)

好吧,Kotlin 又是只用一行就解决了问题……

咱们重点看一下参数列表:

  • 参数列表中定义了三个参数,都使用 val 关键字修饰,说明要使用它们建立成员变量并初始化;

  • name 和 age 参数后面都使用 = 默认值 的方法声明了它们的默认值,在调用主构造函数的时候,若是不传入参数,就使用指定的默认值。

对于构造函数的第二种类型:使用没有关系的参数列表,但存在调用关系,Kotlin 中的处理方式是使用次构造函数委托主构造函数。咱们接着改一下 Person 类:

public class Person {
  long id;
  String name = "";

  public Person(long id) {
    this.id = id;
  }
  public Person(String name) {
    this(name.hashCode());
    this.name = name;
  }}

使用 Kotlin,咱们能够这样写:

class Person(val id: Long) {
  var name: String = ""

  constructor(name: String) : this(name.hashCode().toLong()) {
    this.name = name
  }}

首先,咱们在主构造函数里声明了并初始化了成员变量 id,而后在类体内使用 constructor 关键字定义一个带有一个 String 类型参数的 次构造方法,这个次构造方法先调用主构造函数,而后将参数赋给成员变量。这里有几个须要注意的地方:

  1. 若是类已经有了一个主构造函数,那么全部的次构造函数都要直接或间接地委托给主构造函数。也能够先委托给其余的次构造函数,再由它们委托给主构造函数。全部的次构造函数都会先调用主构造函数,再执行本身特有的代码。写法:

    constructor([参数列表]): this([参数列表]) {/*……*/}
  2. 次构造函数不能在参数列表中声明并初始化成员变量,这也是上面“name: String”前面为何没有 val 的缘由。并且由于次构造函数会改动 name 的值,因此 name 必须声明为 var。

相关文章
相关标签/搜索