kotlin doc 之 类和继承

类和继承

class Invoice{

}

类的声明包括 class关键字 类名 类头(第一构造函数) 类体。并且类头和类体是可以省略的

class Empty

构造函数

在kotlin中,一个类有一个“第一”构造函数和多个“第二”构造函数。并且第一构造函数是类头的一部分,被放置在类名的后面。

class Persion constructor(firstName : String){

}

如果第一个构造函数没有注解或者其他可见标识,则可以将关键字constructor省略掉

class Persion(firstName : String){

}

第一构造函数不包含任何代码。初始化的代码放在init{} 方法体中。

class InitOrderDemo(name : String){
    val firstProperty = "the first Property : $name".also(::println)

    init{
        println("the first initializer block that prints ${name}")
    }

    val secondProperty = "the second Property : ${name.length}".also(::println)

    init{
        println("the second initializer block that prints ${name.leght}")
    }
}

另外需要注意的是,第一构造函数的参数不紧能够在初始化块(init)中使用,还可以在成员变量初始化的时候使用。

class Customer(name : String){
    val customerKey = name.toUpperCase()
}

实际上,为了声明成员属性并且在第一构造函数中初始化它们,kotlin提供以下简介的格式:

class Person(val firstName : String , val lastName : String , var age : Int ){

}

可变的使用var , 只读的使用 val

如果第一构造函数,有修饰符,则关键字constructor不能够省略

class Customer public @Inject constructor(name : String){

}
第二构造函数
class Person{
    costructoer(parent : Person){
        parent.children.add(this)
    }
}

如果一个类有第一构造函数,每一个第二钩构造构造函数需要委托到第一构造函数(无论是直接,还是简介)

class Person(name : String){
    constructor(nameString , parent : Person):this(name){
        parent.children.add(this)
    }
}

注意init 块中方法的执行在第二构造函数方法体执行之前执行。即使该类没有显示标明第一构造函数。而是通过隐式代理。

class Constructors{
    init{
        println("initializer block")
    }

    constuctor(i : Int){
        println(" a second constructor")
    }
}

执行结果: initializer block
          a second constructor 

默认情况一下,一个非抽象类,有一个公有无参构造函数,如果不想用户创建该类实例,可将构造函数私有化

class God private constructor(){
    //这样就能够造神了    
}

注意:在JVM中,第一构造函数的所有参数都有默认值,编译器将会生成一个,使用这些默认值的无参构造构造函数。

class Customer(val customerName : String = "")

创建类的实例对象

为了创建类的实例,我们需要调用构造函数

val invoice = Invoice()

val customer = Customer("Lebron James")

注意: kotlin中没有 new 关键字

创建一个嵌套实例,内部类和匿名内部类被声明在嵌套类

类成员

  • 构造函数和初始化器
  • 函数
  • 属性
  • 嵌套和内部类
  • 对象声明

继承

在Kotlin的世界中Any是任何类的父类。

class Example //隐式继承Any

注意:Any并不是Java中的Object类,它只是提供了equals()、hashCode() 和toString()方法。

为了显示继承父类,我们在把父类放在冒号之后。

open class Base(p:Int)

class Derived(p:Int) :Base(p)

这里的open描述符在类中含义与java中的final相反。它允许该类被继承。kotlin 中的是不能够被继承的。

如果子类有一个第一方法,父类必须在那里使用第一构造方法中的参数初始化。

如果一个没没有第一构造函数,每一个第二构造函数必须使用super关键字类初始化父类。

class MyViewView{
    constructor(ctx:Context):super(ctx)

    constructor(ctx:Context,attrs:AttributeSet):super(ctx,attrs)
}

重写方法

正如我们之前提到的,我们坚持显示的说明某些事情。不像java,kotlin需要表明哪些方法可重写

open class Base{
    open fun v(){}

    fun nv(){}
}

class Derived():Base(){
    override fun v(){}
}

在子类进行重写时,关键字override是必不可少的,否则编译器会报错。如果在父类中的方法没有关键字open,在子类中重写该方法,编译器同样会报错,要么使用重写,要么移除掉他。通常在一个final类中 open 某个方法是被禁止的。一言以蔽之,vorride 和 open是成对的。

open class AnotherDerived():Base(){
    final override fun v(){} //禁止该类的子类重写该方法
}

重写属性

重写属性和重写方法是类似的。在父类中声明的属性,如果在类型再次被声明,需要添加关键字override,并且类型是兼容的。每一个声明的属性能够被重写,以getter、setter方法和初始化器。

open class Foo{
    open val x: Int get(){}
}

class Bar1Foo(){
    override val x:Int = 
}

你可以使用一个var属性重写val属性,但是反过来,不成立。这是因为一个val属性本质上声明了一个getter方法,并且重写var在子类另外还生成一个setter方法。

interface Foo{
    val count: Int
}

class Bar1(ovrride val count :Int):Foo


class Bar2:Foo{
    override val count:Int = 0
}

子类的初始化顺序

在一个子类实例的构建过程中,首先被初始化的是基类,在子类初始化之前。

open class Base(val name:String){
    init{
        println("Init  Base")
    }

    open val size:Int = name.lenght.also{
        println("Init size int Base : $it")
    }
}


class Derived(name :String , val lastName:String):Base(name.capitalize().also{
    println("Argument for Base : $it")
}){

    init{
        println("InitiaLizing Derived")
    }

    override val size:Int = (super.size + lastName.length).also{
        println("InitSize in Derived : $it");
    }

}

那也就意味着,截止到父类构造函数被执行时,子类中被声明或者重写的属性尚未初始化。如果有一些属性在父类的初始化逻辑中被使用,可能会出现问题。因此,在设计一个基类时,避免在狗仔函数、属性初始化、init块中使用open成员。

调用父类的实现

子类通过使用super关键实现调用父类函数和属性。

open class Foo{
    open fun f(){
        println("Foo.f()")
    }

    open val x:Int get() = 1

}

class Bar : Foo{
    override fun f(){
        super.f()
        println("Bar.f()")
    }

    overide val x:Int get() = super.x + 1

}

在内部类中访问外部类的父类使用super关键字加上外部类的类名

class Bar:Foo{
    override fun f(){

    }

    override val x:Int get() = 0

    inner class Baz{
        fun g{
            super@Bar.f()
            //[email protected]() 外部类的f()被调用
            println(super@Bar.x)
        }
    }
}

重写的规则

在kotlin中,继承重写的规则如下:
如果一个类多继承了其他类,而在该类的父类中有多个同一签名的方法,则必须在该类中实现重写。可以通过super的形式调用某个父类的方法

open class A{
    open fun f(){
        print("A")
    }
    fun a(){
        print("a")
    }
}

interface B {
    fun f(){
        print("B")
    }

    fun b(){
        print("b")
    }

}

class C():A(),B{
    override fun f(){
        super<A>.f()
        super<B>.f()
    }
}

抽象类

一个类和类中的成员有可能被声明为抽象的。一个抽象成员在该类中是没有实现的。我们不必注明一个抽象类和成员是 open的。
我们可以用一个抽象成员继承自费抽象成员。

open class Base{
    open fun f(){}
}

abstract class Derived:Base(){
    override abstract fun f()
}

Companion Objects 静态

在Kotlin中,不像Java、C#,类中没有静态方法。在大多数情况下,推荐使用包级别的函数替代。

如果你需要写一个函数,该函数的调用不需要类实例,但是能够方位类的内部,你可以在类的内部声明一个 “Object Declaration”的成员。

如果你在类中声明一个 companion object,你将可以像java中调用静态成员一样,调用它。

猜你喜欢

转载自blog.csdn.net/dirksmaller/article/details/80664889