kotlin学习总结之七 类和对象

一. 类的创建

1. 类的声明.

Kotlin 类可以包含:构造函数和初始化代码块、函数、属性、内部类、对象声明。
Kotlin 中使用关键字 class 声明类,后面紧跟类名:

class Apple{  // 类名为 Apple
    // 大括号内是类体构成
}

当类没有结构体的时候,大括号可以省略。如:

class Apple
类的修饰符 描述
abstract 抽象类
final 类不可继承,默认属性
enum 枚举类
open 类可继承,类默认是final的
annotation 注解类
访问权限修饰符 描述
private 仅在同一个文件中可见
protected 同一个文件中或子类可见
public 所有调用的地方都可见
internal 同一个模块中可见

2. 类的构造函数

在kotlin中有两种类型的构造函数:

  • 主构造函数(主构造器)
  • 次级构造函数(次级构造器)

在Kotlin类中只有一个主构造函数(主构造器),而辅助构造函数(次级构造器)可以是一个或者多个。

2.1 主构造函数

主构造函数是类头的一部分:它跟在类名(和可选的类型参数)后。

标准写法:class 类名 construction(参数1,参数2….){  }

如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字,否则不能省略。
不能省略:

class Person private constructor(name:String){
 val mName: String = name
 ...
}

可以省略:

class Person(name: String) {
 val mName: String = name
  ...
}

如果不需要将构造函数中参数同时作为类属性,也可以写成如下形式(constructor表示构造函数,里面执行初始化的处理):

class Person{
    constructor(name:String){
    ....
    }
}

由于主构造函数没有函数体。如果需要在主构造函数中编写代码该怎么做?初始化的代码可以放 到以 init 关键字作为前缀的初始化块(initializer blocks)中,可以在这里编写要在主构造函数中完成的业务,init{...}中能使用构造函数中的参数:

class Person(username: String, age: Int){
    private val username: String
    private var age: Int
    init{
        this.username = username
        this.age = age
    }
}

注意,主构造的参数可以在初始化块中使用。它们也可以在 类体内声明的属性初始化器中使用:

class Person(name: String) {
    val name= name+"..."
    ...
}

事实上,声明属性以及从主构造函数初始化属性,Kotlin 有简洁的语法:

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

与普通属性一样,主构造函数中声明的属性可以是 可变的(var)或只读的(val)。

2.2 次构造函数

类也可以声明前缀有 constructor的次构造函数,可以声明多个次构造函数:

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

(1)kotlin声明了主构造器
如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数 用 this 关键字即可:

class Person (name : String?,age : Int) {
    var name : String?
    var age : Int
    init {
        println("执行初始化块")
        this.name = name
        this.age = age
    }
    constructor() : this(null,0) {//直接委托调用主构造器

    }
    constructor(name : String) : this() {//间接委托调用主构造器
        
    }
}

(2)kotlin没声明主构造器
kotlin没声明主构造器,重载构造器不需要调用当前类的其他构造器,调用构造器时也会执行初始化块。

class Person {
    var name : String?
    var age : Int
    init {
        println("执行初始化块")
    }
    //在各自的构造器中对属性进行赋值
    constructor() {
        this.name = null
        this.age = 0
    }

    constructor(name : String) {
        this.name = name
        this.age = 0
    }

    constructor(name : String, age : Int) {
        this.name = name
        this.age = age
    }
}

如果一个非抽象类没有声明任何(主或次)构造函数,它会有一个生成的 不带参数的主构造函数。构造函数的可见性是 public。如果你不希望你的类 有一个公有构造函数,你需要声明一个带有非默认可见性的空的主构造函数(此时这个类不能够实例化):

class Person private constructor () {

}

注意:在 JVM 上,如果主构造函数的所有的参数都有默认值,编译器会生成 一个额外的无参构造函数,它将使用默认值。

  • 1、当只写了主构造函数,没有次构造函数,就会覆盖了默认的空造函数,此时创建对象只能通过你写的主构造函数来调用

  • 2、当只写了次构造函数,不写主构造函数,创建对象必须通过次构造函数调用,若不写空的参数构造函数,则调用空的构造函数会报错

  • 3、当写了既写了主构造,又写了次构造函数,可以直接调用主构造函数或者调用次构造函数创建对象。次构造函数最终都是调用主构造函数来创建对象的。

二.类的继承.

在 Kotlin 中所有类都有一个共同的超类 Any,这对于没有超类型声明的类是默认超类:

class Person // 从 Any 隐式继承

Any不是 java.lang.Object;尤其是,它除了 equals()、hashCode()和toString()外没有任何成员。

在Kotlin中,所有类在默认情况下都是无法被继承的,简而言之,就是说Kotlin中,所有类在默认情况下都是final的,但如何才能被继承,Kotlin给我们提供了一个关键字open,只有被open修饰的类才可以被继承(关键字open的作用与final相反),否则编译器会报错。

open class Parent(p: Int)//申明该类是open的

class Child(p: Int) : Parent(p)//继承open类

覆盖方法:

//父类
open class Parent{
    open fun method(){}
    fun unOpen(){  //没有open显示指定,这个函数不能被重写覆盖,
        println("unOpen")
    }
}

//子类
class Child: Parent() {
    ///这里必须使用override关键字来修饰,
    override fun method() {
        super.method()
    }
   final override fun open(){  //Child的子类不可再被重写覆盖
      super.open()
    }
}

覆盖属性和覆盖方法类似。

三.抽象类.

和Java一样,在kotlin中,抽象类用关键字abstract修饰,抽象类的成员可以在本类中提供实现,也可以不实现而交给子类去实现,不实现的成员必须用关键字abstract声明:

abstract class AbsBase{
    abstract fun  method()
}

在kotlin中,被继承的类需要用关键字open声明,表明该类可以被继承,但是抽象类或者抽象函数是不用 open 标注的。但是如果子类要实现抽象类的非抽象函数,需要在抽象类中将其声明为open。

  • 抽象类就是为继承设计的,因此即使不用open关键字修饰,抽象类也是可以被继承的。

  • 使用abstract关键字修饰的抽象函数也可以不加open。

  • 抽象类中的非抽象方法如果不用open修饰的话,不能被子类覆盖。

abstract class AbsBase{
    abstract fun  method()   // 如果子类要实现需声明为抽象 
    open fun method1(){//非抽象方法如果要类子类实现,需要声明为open
        println("输出")
    }
}

class Child : AbsBase() {
    override fun method() {
      //抽象类的实现    
    }
    override fun method1() {
        super.method1()
        println("子类实现")
    }
}

四.接口

定义.

接口用interface作为关键字,基本上和Java的接口类似,但是kotlin的接口不仅可以有抽象方法,也可以有已实现的方法。

interface Animal {
    val kind: String
    val color: String

    fun doing()

    fun eat() {
        println("吃骨头")
    }
}

class Dog : Animal {
    
    override val kind = "小黑狗"
    override val color = "黑色的"

    override fun doing() {
        println("正在玩")
    }
}

接口和抽象类区别.

  • 抽象类可以为属性设置值和getter、setter,而接口不可以。
  • 抽象类和接口中都可以有默认的实现方法,但接口中的方法不能有状态,即涉及到属性的值需要到实现类中设置。
  • 子类只能继承一个父类,但可以实现多个接口。

解决覆盖冲突.

我们可以继承多个接口,可能会出现继承了同一个方法的多个实现。调用应该使用super<类名>.同名方法的形式调用,举个例子:

interface A {
    fun foo() {
        println("A")
    }
    fun bar()
}

interface B {
    fun foo() {
        println("B")
    }
    fun bar() {
        println("bar")
    }
}

class C : A {
    override fun bar() {
        println("C.bar")
    }
}

class D : A, B {
    override fun foo() {
        super.foo()
        super.foo()
        println("D.foo")
    }

    override fun bar() {
        super.bar()
    }
}

fun main(args: Array) {
    var d = D()
    d.foo()
    d.bar()
}

你可能感兴趣的:(kotlin学习总结之七 类和对象)