kotlin学习之路:继承(代码片段)

rockyou666 rockyou666     2023-04-05     139

关键词:

文章目录

前言

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

一. 类的继承

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

// 父类Person
open class Person()

// 子类Man
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的快捷创建方式:
首先在类的内部点击鼠标右键,弹出如下菜单:

在弹出的菜单中点击Generate按钮,再弹出以下菜单:

最后点击Secondary Constructor按钮,选择你需要重写的构造函数:

通过这种快捷方式可以快速的重写父类的所有主次构造函数。

三. 重写和重载

这一章节重点讲解重写,至于重载,可以看我之前的文章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()

输出结果:

eat!

记住无论是super还是this@后面一定是子类!

kotlin学习之类与对象继承与构造函数(代码片段)

类与对象首先创建一个Person类。右击包→New→KotlinFile/Class,在弹出的对话框中输入“Person”。对话框在默认情况下自动选中的是创建一个File,File通常是用于编写Kotlin顶层函数和扩展函数的,我们可以点击展开下拉列... 查看详情

android开发学习之路--kotlin之类及对象

前言1类和继承1.1类classMyClass1.2构造函数一个主构造函数以及多个二级构造函数classPersonconstructor(firstName:String)//也可以省略constructor,写成:classPerson(firstName:String)在@Inject的时候不能省略constructorclassPerson@In 查看详情

kotlin语法学习(代码片段)

文章目录Kotlin语法学习(二)面向对象编程类和对象继承和构造函数接口访问修饰符数据类和单例Kotlin语法学习(二)面向对象编程类和对象创建一个Person类openclassPerson/***姓名*/varname=""/***年龄*/varage=0/***人的吃饭方法*/fune... 查看详情

kotlin学习笔记之面向对象(代码片段)

...ff0c;使用interface表示,示例代码:与java一样,kotlin定义类时要遵循单继承多实现的原则(即只能继承一个父类,可以实现多个接口)kotlin中定义的类和方法默认都是final的,不可重写,如果要实现重... 查看详情

typescript入门学习之路(代码片段)

TypeScript学习之路TypeScript学习之路安装typescript环境typescript起步typescript开发工具vscode自动编译.ts文件typescript中的数据类型typescript中的函数typescript中类的定义继承javascript中的继承typescript中的继承typescript类的静态属性和静态方法t... 查看详情

kotlin语言中的继承与构造函数(详解)(代码片段)

        学习kotlin时,有很多小伙伴无法理解为何有的继承结构父类后面需要加括号,有时候却不用加括号,本篇文章小编将为你们解除这个困惑!顺便为大家详解一下kotlin中的主构造函数和次构造函数。   ... 查看详情

go语言学习之路(代码片段)

Go语言学习之路(二)面对对象编程思想抽象封装继承接口文件命令行参数Json序列化反序列化(unmarshal)单元测试RedisRedis简介Redis基本使用Go连接redisRedis连接池Go面试题goroutine和channel(275-283)协程goroutine... 查看详情

go语言学习之路(代码片段)

Go语言学习之路(二)面对对象编程思想抽象封装继承接口文件命令行参数Json序列化反序列化(unmarshal)单元测试RedisRedis简介Redis基本使用Go连接redisRedis连接池Go面试题goroutine和channel(275-283)协程goroutine... 查看详情

学习kotlin-扩展函数(代码片段)

...类的功能而无需接触它们的代码的函数。换句话说,Kotlin中的扩展函数允许我们通过添加新函数来扩展类的功能。该类不必属于我们(可以是第三方库),也不需要我们继承该类。举个很简单的例子来理解。funInt.... 查看详情

kotlin学习笔记之面向对象(代码片段)

...ff0c;使用interface表示,示例代码:与java一样,kotlin定义类时要遵循单继承多实现的原则(即只能继承一个父类,可以实现多个接口)kotlin中定义的类和方法默认都是final的,不可重写,如果要实现重... 查看详情

android开发学习之路--kotlin之基础语法

前言java代码写久了自然会想换个语言玩一下,而且kotlin作为android的官方语言,总是要学习下的,故这里先来学习下一些基本语法吧,打个小基础,后面才能游刃有余地开发。1基础语法这里省略了很多一般的... 查看详情

kotlin初学者枚举类-密封类-数据类-继承(代码片段)

...介:CSDN博客专家、华为云享专家认证系列专栏:Kotlin初学者学习交流:三人行必有我师焉;择其善者而从之,其不善者而改之。目录一、枚举类1.1创建枚举类1.2获取枚举相关信息1.3枚举类添加属性1.4定义函数1... 查看详情

代码的认爹之路:面向对象继承(代码片段)

面向对象-继承前言:​Hello,各位同学朋友大家好啊,今天给大家分享的技术呢,是面向对象三大特征之一的继承.我们今天主要按照以下几个点,展开继承的讲解目录:继承的介绍继承的好处和弊端继承中成员访问特点-成员变量继承中... 查看详情

android学习之《第一行代码》第三版笔记kotlin继承时的括号到底写不写(代码片段)

内容学习来源:第2章探究新语言,快速入门Kotlin编程目录一、问题来源二、继承与构造函数(一)前置知识(二)括号代表了什么(三)继承时,什么时候不要加括号1.子类的括号不用写:... 查看详情

kotlin小悟-这个继承有点不一样(代码片段)

今天聊聊kotlin中关于构造函数的一个话题。我们知道,在kotlin中构造函数分为主构造函数和从构造函数,关于详细的内容,可以点击下面的文章链接了解。Kotlin系列之主构造方法和初始化语句块Kotlin系列之从构造方法... 查看详情

android开发学习之路--kotlin之android开发使用心得和注意事项

前言1@Autowired需要@JvmField注解@Autowired(name="test")@JvmFieldvarcurrentDate:Long=0当我们用到类似于阿里的ARouter的时候,会发现@Autowired不起作用,其实是注入代码为了减少反射,使用的字段赋值的方式来注... 查看详情

kotlin开发基础(代码片段)

...一时间获取我的最新文章,请关注公众号:技术特工队Kotlin开发基础(一)Kotlin开发基础(二)Kotlin开发基础(三)类本篇文章主要介绍类的继承,接口,复写,单例,以及object属性的介... 查看详情

kotlin类的继承(代码片段)

与Java不同,kotlin使用冒号,而Java中使用extends,注意冒号后面需要调用夫类的构造器。属于单继承,使用open关键字允许继承classpackageloaderman.demoopenclassPerson(name:String)protectedvarmName:String=namefungetName():StringreturnmNamepackageloaderman.democlass... 查看详情