Kotlin学习之路(六):继承

这篇具有很好参考价值的文章主要介绍了Kotlin学习之路(六):继承。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

Kotlin的继承和Java的继承一样都是单继承,区别在于Kotlin用:来代替了extends

一. 类的继承

Kotlin用:表示继承,Java用exteds表示继承。

// 父类Person
open class Person()

// 子类Man
class Man() : Person()

// 简写
class Man: Person()

上面是最简单的Kotlin继承的例子。这里还需要注意的是在Kotlin中类默认都是final的(代表该类不让继承),需要在class关键词前面加上open,表示该类可以被其他类继承。和Java一样Kotlin也是单继承的。
和Java的继承一样,子类可以使用父类的属性和方法:

open class Person {
    var name: String = ""

    var age: Int = -1

    fun eat(){
        println("eat food!")
    }
}

class Man() : Person()

fun main() {
    val man = Man()
    man.age = 22
    man.name = "Tom"
    println("age is ${man.age}")
    println("name is ${man.name}")
    man.eat()
}

输出结果:

age is 22
name is Tom
eat food!

从上面的代码可以看出,子类Man可以使用父类Person的属性和方法。

二. 继承中的构造函数

Kotlin在继承情况的下,构造函数的语法是怎么样的?首先来看看Java是如何处理的。

2.1 继承中Java的构造函数

Java是通过super来实现对父类构造函数的访问和传值,类似如下代码:

// Person类(父类)
public class Person {
    private String name;

    private int age;

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

// Man类(子类)
public class Man extends Person{

    public Man(String name, int age) {
        super(name, age);
    }
}

通过关键字super可以将参数传递给父类,AndroidStudio中可以通过点击Create constructor matching super自动生成构造函数。接下来看看Kotlin是如何处理这个问题的。

2.2 继承中Kotlin的构造函数

在Kotlin中实现这种功能,只需要极少的代码量就可以实现:

// Person
open class Person(var name:String, var age:Int) {
    // Empty Code
}

// Man类
class Man(name: String, age: Int): Person(name, age) {
	// Empty Code
}

fun main() {
    val man = Man("Tom", 22)
    println("name is ${man.name} age is ${man.age}")
}

打印结果:

name is Tom age is 22

通过Man类的构造函数传递参数nameage,再通过: Person(name, age)传递到父类。
有人可能要问了,如果不加上后面的: Person(name, age)是否可行?
显然是不行的,Man类的构造函数没有var,那么它们就只是Man类构造函数的参数,不是类的成员变量。
那么如果Man类加上var是否可以?
答案也是不行的,这样父类和子类就会有两套成员变量,肯定会报错的。但是,我们可以重写父类的成员变量,后面会详细讲到。

2.3 Kotlin多个构造函数的情况

Kotlin是有主次构造函数之分的,如果一个父类有多个构造函数,那么子类是如何继承的呢?
第一种写法:

// Person类,这里有三个构造函数
open class Person(var name: String, var age: Int) {
    constructor(name: String): this(name, -1)
    constructor(age: Int): this("", age)
}

// Man类
class Man(name: String, age: Int) : Person(name, age) {
	// Empty Code
}

父类有多个构造函数的情况,可以和上面一样这么写。但是,这样就不能以次构造函数的方式去创建一个对象。

fun main(){
    val man = Man("Tom") // 报错!
    println("name is ${man.name} age is ${man.age}")
}

造成这种情况的原因是,我们只重写了父类的主构造函数,没有重写父类的次构造函数。针对这种情况,我们可以这样写。同样是上面的例子,代码如下:

// Person类,这里有三个构造函数
open class Person(var name: String, var age: Int) {
    constructor(name: String): this(name, -1) // 次构造函数
    constructor(age: Int): this("", age) // 次构造函数
}

// Man类
class Man: Person {
    constructor(name: String, age: Int) : super(name, age) // 对应Person类的主构造函数
    constructor(name: String) : super(name) // 对应Person类的constructor(name: String)
    constructor(age: Int) : super(age) // 对应Person类的constructor(age: Int)
}

直接将父类的三个次构造函数再复写一遍,用super指向父类的构造函数就行了。
为了方便快速的构建,可以利用AndroidStudio的快捷创建方式:
首先在类的内部点击鼠标右键,弹出如下菜单:
Kotlin学习之路(六):继承
在弹出的菜单中点击Generate按钮,再弹出以下菜单:
Kotlin学习之路(六):继承

最后点击Secondary Constructor按钮,选择你需要重写的构造函数:
Kotlin学习之路(六):继承
通过这种快捷方式可以快速的重写父类的所有主次构造函数。

三. 重写和重载

这一章节重点讲解重写,至于重载,可以看我之前的文章Kotlin学习之路(三):函数
中的1.3小结

3.1 方法的重写

代码还是上面的例子,只不过父类里面多一个测试方法:

// Person
open class Person(var name:String, var age:Int) {

    open fun testMethod(){ // 注意这里加上了open,表示可以被重写。默认情况下是fina
        println("父类test!")
    }
}

// Man类
class Man(name: String, age: Int): Person(name, age) {

    override fun testMethod(){ // 类似Java的override重写标志
        println("子类test!")
    }
}

fun main(){
    val man = Man("Tom", 22)
    man.testMethod()
}

输出结果:

子类test!

这里需要着重介绍的一点的是,Kotlin的方法不加修饰符之前都是public final 的,特别是final ,所以在需要被重写的方法中加上open修饰符。
重写方法需要遵循 两同、两小、一大的原则。

  • 两同:方法名相同,参数类型相同
// Person
open class Person(var name:String, var age:Int) {

    open fun testMethod(str: String){
        println("父类test! $str")
    }
}

// Man类
class Man(name: String, age: Int): Person(name, age) {

    override fun testMethod(str: String){
        println("子类test! $str")
    }
}

fun main(){
    val man = Man("Tom", 22)
    man.testMethod("aaa")
}
  • 两小:子类返回值类型比父类返回值类型(返回值类型范围)小或相等、子类抛出异常类型比父类小或相等
// Person
open class Person(var name:String, var age:Int) {

    open fun testMethod(): Any?{
        println("父类test!")
        return null
    }
}

// Man类
class Man(name: String, age: Int): Person(name, age) {

    override fun testMethod(): String{
        println("子类test!")
        return "test"
    }
}

上面的例子父类的方法的返回值类型是Any?,子类的方法的返回值类型是String,从继承关系看,String继承于Any?,范围自然是Any?大于String。不过这种情况一般用的不多。

  • 一大:子类的修饰符权限比父类的修饰符权限大或相等
// Person
open class Person(var name:String, var age:Int) {

    protected open fun testMethod(): Any?{
        println("父类test!")
        return null
    }
}

// Man类
class Man(name: String, age: Int): Person(name, age) {

    override fun testMethod(): String {// 如果这里改为private就会报错
        println("子类test!")
        return "test"
    }
}

3.2 属性的重写

属性的重写和方法的重写类似:

// Person
open class Person(var name:String, var age:Int) {
    open var test = -1 // 和方法一样,加上了open
}

// Man类
class Man(name: String, age: Int): Person(name, age) {
    override var test = 0 // 这里加上了override标志
}

fun main(){
    val man = Man("Tom", 22)
    println("test value is ${man.test}")
}

输出结果:

test value is 0

从结果可以看出父类的成员变量test被子类重写了。一般情况下这种功能较少使用,重写属性可能会破坏里氏替换原则(子类可以扩展父类的功能,但不能改变父类原有的功能)

四. super关键字

Kotlin和Java一样也是有super关键字的。和Java的用法也是类似的,都是指代父类,用法也和Java相似。

4.1 简单用法

// Person
open class Person(var name:String, var age:Int) {
    // Empty Code
}

// Man类
class Man(name: String, age: Int): Person(name, age) {

    fun test(){
        println("name is ${super.name}")
    }
}

fun main(){
    val man = Man("Tom", 22)
    man.test()
}

输出结果:

name is Tom

上面的例子多少有点脱裤子放屁的意思,实际上可以去掉super,这里只是为了演示。它的主要作用还是用于方法重写上面。

4.2 复杂情况下的用法

这里介绍相对复杂的情况下的super的用法。

4.2.1 子类重写方法中使用super

在重写方法中使用super的方式基本和Java一样:

// Person
open class Person(var name:String, var age:Int) {

    open var test = -1

    open fun testMethod(){
        println("父类test!")
    }
}

// Man类
class Man(name: String, age: Int): Person(name, age) {

    override var test = 0

    override fun testMethod() {
        super.testMethod() // 和Java的用法一样,调用父类Person的方法testMethod
        println("test value is ${super.test}") // 调用的是父类Person的test
    }
}

fun main(){
    val man = Man("Tom", 22)
    man.testMethod()
}

输出结果:

父类test!
test value is -1

4.2.2 子类选择性调用父接口/父类的方法

当Kotlin的类继承父类和多个接口的时候,这就需要区分super调用的是哪一个父类的方法。Kotlin中通过super<父类/父接口>.方法方式调用父类/接口的方法

// Australopithecus接口,写法和Java类似
interface Australopithecus{

    fun testMethod(){
        println("接口test!")
    }
}

// Person
open class Person {

    open fun testMethod(){
        println("父类test!")
    }
}

// Man类,既继承了父类Man,也实现了接口Australopithecus
class Man: Person(), Australopithecus {

    override fun testMethod() {
        super<Australopithecus>.testMethod()
        super<Person>.testMethod()
    }
}

fun main(){
    val man = Man()
    man.testMethod()
}

输出结果:

接口test!
父类test!

4.2.3 子类内部类调用父类方法

这种情况在于子类的内部类调用父类的方法,写法类似这样:super@子类.方法

// Person
open class Person {

    open fun testMethod(){
        println("父类test!")
    }
}

// Man类
class Man: Person() {

	// 被inner修饰的内部类是非静态内部类,静态内部类显然不能访问非静态内部类的成员方法
    inner class Heart{
    
        fun testMethod(){
            super@Man.testMethod()
        }
    }
}

fun main(){
    Man().Heart().testMethod()
}

输出结果:

父类test!

其实this在内部类中也有类似的用法:

// Man类
class Man {

    fun eat(){
        println("eat!")
    }

    inner class Heart{

        fun testMethod(){
            this@Man.eat() // 调用外部类的方法,这种操作更加常见
        }
    }
}

fun main(){
    Man().Heart().testMethod()
}

输出结果:文章来源地址https://www.toymoban.com/news/detail-418004.html

eat!

到了这里,关于Kotlin学习之路(六):继承的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 课程学习前言

    app 抓包分析可以看到有签名有加固,毕竟需要 APK 去访问服务、获取数据,都需要 APK 有完整的信息,而这些信息、代码经过各种加密,还是放在 APK 里面。说白了,就是门锁紧了,钥匙藏在门口某个地方,也许就是地垫下面 逆向流程 拿到 App 应用的 apk ; 使用工具进行查壳

    2024年02月06日
    浏览(45)
  • 【C++】C++学习前言

    C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(objectoriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。

    2024年03月12日
    浏览(55)
  • Gowin FPGA学习记录——前言

            好久没有写博客了,想想是不是又该写点啥东西了么,准备写点国产FPGA的使用经历吧                  得益于目前国内的政策对国产化芯片扶持,越来越要求核心器件能够自主可控,因此作为核心芯片FPGA,国产FPGA的势头也发展很快。          现在FPGA的这

    2024年02月16日
    浏览(42)
  • 【自制C++深度学习框架】前言

    此GitHub项目是一个初学者的深度学习框架,使用C++编写,旨在为用户提供一种简单、易于理解的深度学习实现方式。以下是本项目的主要特点和功能: 计算图:使用计算图来描述深度学习模型的计算过程,利用计算图将神经网络的计算过程视为一个有向无环图。通过构建计算

    2024年02月07日
    浏览(43)
  • FPGA学习实践之旅——前言及目录

    很早就有在博客中记录技术细节,分享一些自己体会的想法,拖着拖着也就到了现在。毕业至今已经半年有余,随着项目越来越深入,感觉可以慢慢进行总结工作了。趁着2024伊始,就先开个头吧,这篇博客暂时作为汇总篇,记录在这几个月以及之后从FPGA初学者到也算有一定

    2024年02月03日
    浏览(58)
  • 大数据、人工智能、机器学习、深度学习关系联系前言

    1.大数据和人工智能关系 2.机器学习、深度学习、人工智能关系 3.监督学习、无监督学习、半监督学习、强化学习、迁移学习关系 4.机器学习具体内容 1.数据驱动的人工智能 :人工智能系统需要大量的数据来进行训练和学习。大数据提供了海量的信息,可以用于训练机器学习

    2024年02月12日
    浏览(62)
  • 【Kotlin】类的继承 ② ( 使用 is 运算符进行类型检测 | 使用 as 运算符进行类型转换 | 智能类型转换 | Any 超类 )

    在 Kotlin 中 , 如果不确定一个 实例对象的类型 , 可以 使用 is 运算符进行判定 , 使用方法 上述用法可以判定 实例对象 是否是 判定类型 , 如果是 返回 true , 反之 返回 false ; 代码示例 : 在下面的代码中 , 调用 student is Person 判断 student 对象是否是 Person 类型 ; 执行结果 : 将 子类

    2024年02月03日
    浏览(47)
  • 【C++初阶(一)】学习前言以及命名空间

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C++初阶之路⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习排序知识   🔝🔝 对于复杂的问题,规模较大的程序 需要高度的抽象和建模时 C语言不再适合应用于这种场景 于是在1982年 C++创始人 Bjarne Stroustrup 在C语言

    2024年02月11日
    浏览(52)
  • Linux tcp/ip 网路协议栈学习-00 前言

    Linux tcp/ip 网路协议栈学习-00 前言 目录 Linux  tcp/ip 网路协议栈学习-00 前言 (1)预备知识  (2)前置知识 (3)学习目标 (4)总结     (1)预备知识  好工具事半功倍,做任何事情都需要有方法和工具,同样,阅读 Linux 内核源码也是如此。由于当前内核源码非常庞大,学习上,不能一

    2024年04月26日
    浏览(44)
  • 【C++初阶(一)】学习前言 命名空间与IO流

    本专栏内容为:C++学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C++。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:C++ 🚚代码仓库:小小unicorn的代码仓库🚚 🌹🌹🌹关注我带你学习编程知识 C++是基于C语言而产生的,它既可以进行C语言的

    2024年02月08日
    浏览(96)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包