Kotlin 学习笔记 (Classes and Objects)

本文详细介绍了Kotlin中的类和继承,包括构造函数、类的实例化、继承规则、抽象类、伴生对象等。此外,还涵盖了属性和字段、接口、可见性修饰符、扩展、数据类、嵌套和内部类、枚举类以及对象表达式和声明等核心概念,为深入理解Kotlin的面向对象编程提供了全面的知识。
摘要由CSDN通过智能技术生成

Classes and Objects

Classes and Inheritance

The class declaration consists of the class name, the class header (specifying its type parameters, the primary constructor etc.) and the class body, surrounded by curly braces. Both the header and the body are optional; if the class has no body, curly braces can be omitted.

class Invoice {
    /*...*/ }
class Empty

Constructors

primary constructor

The primary constructor is part of the class header,

class Person constructor(firstName: String) {
    /*...*/ }

If the primary constructor does not have any annotations or visibility modifiers, the constructor keyword can be omitted:

class Person(firstName: String) {
    /*...*/ }

The primary constructor cannot contain any code. Initialization code can be placed in initializer blocks, which are prefixed with the init keyword.

During an instance initialization,the initializer blocks are executed in the same order as they appear in the class body, interleaved with the property initializers:

class InitOrderDemo(name: String) {
   
    val firstProperty = "First property: $name".also(::println)
    
    init {
   
        println("First initializer block that prints ${
     name}")
    }
    
    val secondProperty = "Second property: ${
     name.length}".also(::println)
    
    init {
   
        println("Second initializer block that prints ${
     name.length}")
    }
}

fun main() {
   
    InitOrderDemo("hello")
}

and the result is :

First property: hello
First initializer block that prints hello
Second property: 5
Second initializer block that prints 5

Note that parameters of the primary constructor can be used in the initializer blocks. They can also be used in property initializers declared in the class body:

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

In fact, for declaring properties and initializing them from the primary constructor, Kotlin has a concise syntax:
Much the same way as regular properties, the properties declared in the primary constructor can be mutable (var) or read-only (val).

If the constructor has annotations or visibility modifiers, the constructor keyword is required, and the modifiers go before it:

class Person(val firstName: String, val lastName: String, var age: Int) {
    /*...*/ }
class Customer public @Inject constructor(name: String) {
    /*...*/ }

Secondary constructors

The class can also declare secondary constructors, which are prefixed with constructor:

class Person {
   
    var children: MutableList<Person> = mutableListOf<>()
    constructor(parent: Person) {
   
        parent.children.add(this)
    }
}

If the class has a primary constructor, each secondary constructor needs to delegate to the primary constructor, either directly or indirectly through another secondary constructor(s). Delegation to another constructor of the same class is done using the this keyword:

class Person(val name: String) {
   
    var children: MutableList<Person> = mutableListOf<>()
    constructor(name: String, parent: Person) : this(name) {
   
        parent.children.add(this)
    }
}

Note that code in initializer blocks effectively becomes part of the primary constructor.== Delegation to the primary constructor happens as the first statement of a secondary constructor==, so the code in all initializer blocks and property initializers is executed before the secondary constructor body. Even if the class has no primary constructor, the delegation still happens implicitly, and the initializer blocks are still executed:

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

    constructor(i: Int) {
   
        println("Constructor")
    }
}

fun main() {
   
    Constructors(1)
}

result is :

Init block
Constructor

NOTE: On the JVM, if all of the parameters of the primary constructor have default values, the compiler will generate an additional parameterless constructor which will use the default values. This makes it easier to use Kotlin with libraries such as Jackson or JPA that create class instances through parameterless constructors.

class Customer(val customerName: String = "")

Creating instances of classes

val invoice = Invoice()

val customer = Customer("Joe Smith")

Inheritatnce

Any is superclass

Any has three methods:equals(), hashCode() and toString(). Thus, they are defined for all Kotlin classes.

class Example // Implicitly inherits from Any

Syntax of class inheritatance

By default, Kotlin classes are final: they can’t be inherited. To make a class inheritable, mark it with the open keyword.

open class Base //Class is open for inheritance

open class Base(p: Int)

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

If the derived class has a primary constructor, the base class can (and must) be initialized right there, using the parameters of the primary constructor.

If the derived class has no primary constructor, then each secondary constructor has to initialize the base type using the super keyword, or to delegate to another constructor which does that. Note that in this case different secondary constructors can call different constructors of the base type:

class MyView : View {
   
    constructor(ctx: Context) : super(ctx)

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

Overriding methods

As we mentioned before, we stick to making things explicit in Kotlin. So, Kotlin requires explicit modifiers for overridable members (we call them open) and for overrides:

open class Shape {
   
    open fun draw() {
    /*...*/ }
    fun fill() {
    /*...*/ }
}

class Circle() : Shape() {
   
    override fun draw() {
    /*...*/ }
}

A member marked override is itself open, i.e. it may be overridden in subclasses. If you want to prohibit re-overriding, use final:

open class Rectangle() : Shape() {
   
    final override fun draw() {
    /*...*/ }
}

Overriding properties

Each declared property can be overridden by a property with an initializer or by a property with a get method.

open class Shape {
   
    open val vertexCount: Int = 0
}

class Rectangle : Shape() {
   
    override val vertexCount = 4
}

You can also override a val property with a var property, but not vice versa

you can use the override keyword as part of the property declaration in a primary constructor.

interface Shape {
   
    val vertexCount: Int
}

class Rectangle(override val vertexCount: Int = 4) : Shape // Always has 4 vertices

class Polygon : Shape {
   
    override var vertexCount: Int = 0  // Can be set to any number later
}

Derived class initialization order

During construction of a new instance of a derived class,the base class initialization is done as the first step (preceded only by evaluation of the arguments for the base class constructor) and thus happens before the initialization logic of the derived class is run.

open class Base(val name: String) {
   

    init {
    println("Initializing Base") }

    open val size: Int = 
        name.length.also {
    println("Initializing size in 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("Initializing size in Derived: $it") }
}

fun main() {
   
    println("Constructing Derived(\"hello\", \"world\")")
    val d = Derived("hello", "world")
}

result is :

Constructing Derived("hello", "world")
Argument for Base: Hello
Initializing Base
Initializing size in Base: 5
Initializing Derived
Initializing size in Derived: 10

It means that, by the time of the base class constructor execution, the properties declared or overridden in the derived class are not yet initialized. If any of those properties are used in the base class initialization logic (either directly or indirectly, through another overridden open member implementation), it may lead to incorrect behavior or a runtime failure. When designing a base class, you should therefore avoid using open members in the constructors, property initializers, and init blocks.

Calling the superclass implementation

Code in a derived class can call its superclass functions and property accessors implementations using the super keyword:
nside an inner class, accessing the superclass of the outer class is done with the super keyword qualified with the outer class name: super@Outer:

open class Rectangle {
   
    open fun draw() {
    println("Drawing a rectangle") }
    val borderColor: String get() = "black"
}

class FilledRectangle : Rectangle() {
   
    override fun draw() {
   
        super.draw()
        println("Filling the rectangle")
    }
	//override property with get method
    val fillColor: String get() = super.borderColor 
}
class FilledRectangle: Rectangle() {
   
    fun draw() {
    /* ... */ }
    val borderColor: String get() = "black"
    
    inner class Filler {
   
        fun fill() {
    /* ... */ }
        fun drawAndFill() 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值