Kotlin 面向对象编程

面向对象编程

类与对象

Kotlin中也是使用class关键字来声明一个类的

Kotlin中实例化一个类的方式和Java是基本类似的,只是去掉了new关键字而已

class Person {
    var name = ""
    var age = 0

    fun eat() {
        println(name + " is eating. He is " + age + " years old.")
    }
}

fun main() {
    val p = Person()
    p.name = "Jack"
    p.age = 19
    p.eat()
}

继承

在Kotlin中任何一个非抽象类默认都是不可以被继承的,相当于Java中给类声明了final关键字

抽象类本身是无法创建实例的,一定要由子类去继承它才能创建实例,因此抽象类必须可以被继承才行,要不然就没有意义了。Kotlin中的抽象类和Java中并无区别

// open关键字表示允许被继承
open class Person {
    var name = ""
    var age = 0

    fun eat() {
        println(name + " is eating. He is " + age + " years old.")
    }
}

// 在Java中继承的关键字是extends,而在Kotlin中变成了一个冒号
class Student : Person() {
    var sno = ""
    var grade = 0
}

fun main() {
    val s = Student()
    s.eat()
}

主构造函数

主构造函数是最常用的构造函数,每个类默认都会有一个不带参数的主构造函数,当然也可以显式地给它指明参数。

主构造函数的特点是没有函数体,直接定义在类名的后面即可。比如下面这种写法:

//由于构造函数中的参数是在创建实例的时候传入的,不像之前的写法那样还得重新赋值,因此可以将参数全部声明成val。
class Student(val sno: String, val grade: Int) : Person() {
}

fun main() {
    // 在对Student类进行实例化的时候,必须传入构造函数中要求的所有参数
    val student = Student("a123", 5)
    student.eat()
}

主构造函数没有函数体,如果想在主构造函数中编写一些逻辑,该怎么办呢? Kotlin给我们提供了一个init结构体,所有主构造函数中的逻辑都可以写在里面:

class Student(val sno: String, val grade: Int) : Person() {
    init {
        println("sno is " + sno)
        println("grade is " + grade)
    }
}

将Person改造一下,将姓名和年龄都放到主构造函数当中

open class Person(val name: String, val age: Int) {
    fun eat() {
        println(name + " is eating. He is " + age + " years old.")
    }
}

class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) {

}

fun main() {
    val student = Student("a123", 5, "Jack", 19)
    student.eat()
}

次构造函数

其实几乎是用不到次构造函数的,Kotlin提供了一个给函数设定参数默认值的功能,基本上可以替代次构造函数的作用

任何一个类只能有一个主构造函数,但是可以有多个次构造函数。次构造函数也可以用于实例化一个类,这一点和主构造函数没有什么不同,只不过它是有函数体的。

Kotlin规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)。

//次构造函数是通过constructor关键字来定义的
class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) {
    //通过this关键字调用了主构造函数
    constructor(name: String, age: Int) : this("", 0, name, age) {
    }
    //通过this关键字调用了上面的次构造函数
    constructor() : this("", 0) {
    }
}

fun main() {
    // 那么现在我们就拥有了3种方式来对Student类进行实体化,
    // 分别是通过不带参数的构造函数、通过带两个参数的构造函数和通过带4个参数的构造函数,对应代码如下所示:
    val student1 = Student()
    val student2 = Student("Jack", 19)
    val student3 = Student("a123", 5, "Jack", 19)
    student1.eat()
}

来看一种非常特殊的情况:类中只有次构造函数,没有主构造函数。这种情况真的十分少见,但在Kotlin中是允许的。当一个类没有显式地定义主构造函数且定义了次构造函数时,它就是没有主构造函数的。结合代码来看一下:

//Student类的后面没有显式地定义主构造函数,同时又因为定义了次构造函数,所以现在Student类是没有主构造函数的。
// 那么既然没有主构造函数,继承Person类的时候也就不需要再加上括号了。
class Student : Person {
    // 由于没有主构造函数,次构造函数只能直接调用父类的构造函数,上述代码也是将this关键字换成了super关键字
    constructor(name: String, age: Int) : super(name, age) {
    }
}

接口

Kotlin中的接口部分和Java几乎是完全一致的

interface Study {
    fun readBooks()
    fun doHomework()
}

//Java中继承使用的关键字是extends,实现接口使用的关键字是implements,而Kotlin中统一使用冒号,中间用逗号进行分隔
// 接口的后面不用加上括号,因为它没有构造函数可以去调用。
class Student(name: String, age: Int) : Person(name, age), Study {
    // Kotlin中使用override关键字来重写父类或者实现接口中的函数
    override fun readBooks() {
        println(name + " is reading.")
    }

    override fun doHomework() {
        println(name + " is doing homework.")
    }
}

/*
这里为了演示一下多态编程的特性,故意将代码写得复杂了一点。
首先创建了一个Student类的实例,本来是可以直接调用该实例的readBooks()和doHomework()函数的,
但是没有这么做,而是将它传入到了doStudy()函数中。
doStudy()函数接收一个Study类型的参数,由于Student类实现了Study接口, 因此Student类的实例是可以传递给doStudy()函数的, 接下来我们调用了Study接口的readBooks()和doHomework()函数, 这种就叫作面向接口编程,也可以称为多态。 */ fun main() { val student = Student("Jack", 19) doStudy(student) } fun doStudy(study: Study) { study.readBooks() study.doHomework() }

Kotlin还增加了一个额外的功能:允许对接口中定义的函数进行默认实现。其实Java在JDK 1.8之后也开始支持这个功能了,因此总体来说,Kotlin和Java在接口方面的功能仍然是一模一样的。

interface Study {
    fun readBooks()
    fun doHomework() {
        println("do homework default implementation.")
    }
}

class Student(name: String, age: Int) : Person(name, age), Study {
    override fun readBooks() {
        println(name + " is reading.")
    }
//    override fun doHomework() {
//        println(name + " is doing homework.")
//    }
}

函数的可见性修饰符

和Java相比变化比较大的部分——函数的可见性修饰符

Java和Kotlin函数可见性修饰符对照表

数据类

在一个规范的系统架构中,数据类通常占据着非常重要的角色,它们用于将服务器端或数据库中的数据映射到内存中,为编程逻辑提供数据模型的支持。或许你听说过MVC、MVP、MVVM之类的架构模式,不管是哪一种架构模式,其中的M指的就是数据类。

数据类通常需要重写equals()、hashCode()、toString()这几个方法。

在一个类前面声明了data关键字时,就表明你希望这个类是一个数据类,Kotlin会根据主构造函数中的参数帮你将equals()、hashCode()、toString()等固定且无实际逻辑意义的方法自动生成

当一个类中没有任何代码时,还可以将尾部的大括号省略

data class Cellphone(val brand: String, val price: Double)

fun main() {
    val cellphone1 = Cellphone("Samsung", 1299.99)
    val cellphone2 = Cellphone("Samsung", 1299.99)
    println(cellphone1)
    println("cellphone1 equals cellphone2 " + (cellphone1 == cellphone2))
}

单例类

在Kotlin中创建一个单例类的方式极其简单,只需要将class关键字改成object关键字即可。

object Singleton {
    fun singletonTest() {
        println("singletonTest is called.")
    }
}

fun main() {
    // 这种写法虽然看上去像是静态方法的调用,但其实Kotlin在背后自动帮我们创建了一个Singleton类的实例,并且保证全局只会存在一个Singleton实例。
    Singleton.singletonTest()
}

  

posted @ 2023-01-13 10:00  草木物语  阅读(38)  评论(0编辑  收藏  举报