Kotlin 2.1.0 入门教程(十五)继承、重写、派生类初始化顺序

继承

所有类都有一个共同的超类 Any,对于没有声明超类型的类来说,Any 是其默认的超类:

// 隐式继承自 Any。
class Example

Any 有三个方法:equals()hashCode()toString()。因此,所有类都定义了这些方法。

默认情况下,类是 final 的,即它们不能被继承。若要使一个类可被继承,需使用 open 关键字标记它:

// 该类可被继承。
open class Base

要声明一个显式的超类型,在类头中把类型放在冒号之后:

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

如果派生类有主构造函数,基类必须根据其参数在该主构造函数中进行初始化。

如果派生类没有主构造函数,那么每个次构造函数都必须使用 super 关键字来初始化基类型,或者委托给另一个能完成此操作的构造函数。注意,在这种情况下,不同的次构造函数可以调用基类型的不同构造函数:

class MyView : View {constructor(ctx: Context) : super(ctx)constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}

方法重写

对可重写的成员和重写操作使用显式修饰符:

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

对于 Circle.draw() 方法,override 修饰符是必需的。如果缺少该修饰符,编译器将会报错。如果一个函数没有 open 修饰符,比如 Shape.fill(),那么在子类中声明具有相同签名的方法是不允许的,无论是否使用 override 修饰符。对于 final 类(即没有 open 修饰符的类)的成员,添加 open 修饰符是没有效果的。

标记为 override 的成员本身是 open 的,因此它可以在子类中被再次重写。如果你想禁止再次重写,可以使用 final 修饰符:

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

属性重写

属性的重写机制和方法的重写机制类似。在超类中声明的属性,若要在派生类中重新声明,必须在前面加上 override 关键字,并且它们的类型必须兼容。每个已声明的属性可以被一个带有初始值设定项的属性或一个带有 get 方法的属性重写:

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

你也可以用一个 var 属性重写一个 val 属性,但反之则不行。这是允许的,因为 val 属性本质上声明了一个 get 方法,而将其重写为 var 属性时,还会在派生类中额外声明一个 set 方法。

注意,你可以在主构造函数中把 override 关键字作为属性声明的一部分来使用:

interface Shape {val vertexCount: Int
}class Rectangle(override val vertexCount: Int = 4) : Shapeclass Polygon : Shape {override var vertexCount: Int = 0
}

派生类初始化顺序

在创建派生类的新实例的过程中,基类的初始化是第一步(仅在对基类构造函数的参数求值之后进行),这意味着基类的初始化会在派生类的初始化逻辑执行之前完成。

open class Base(val name: String,) {init { println("Initializing a base class") }open val size: Int = name.length.also {println("Initializing size in the base class: $it")}
}class Derived(name: String, val lastName: String,): Base(name.replaceFirstChar { it.uppercase() }.also { println("Argument for the base class: $it") })
{init { println("Initializing a derived class") }override val size: Int = (super.size + lastName.length).also {println("Initializing size in the derived class: $it")}
}fun main() {// Argument for the base class: Aaa// Initializing a base class// Initializing size in the base class: 3// Initializing a derived class// Initializing size in the derived class: 6Derived("aaa", "bbb")
}

这意味着当基类构造函数执行时,派生类中声明或重写的属性尚未初始化。在基类的初始化逻辑中使用这些属性(无论是直接使用,还是通过另一个被重写的开放成员的实现间接使用)可能会导致错误的行为或运行时故障。因此,在设计基类时,你应该避免在构造函数、属性初始化器或初始化块中使用开放成员。

调用超类实现

派生类中的代码可以使用 super 关键字调用其超类的函数和属性访问器实现:

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")}val fillColor: String get() = super.borderColor
}

在内部类中,如果要访问外部类的超类,可以使用带有外部类名限定的 super 关键字,即 super@Outer 这种形式。下面通过具体的代码示例来详细解释:

open class SuperClass {open fun doSomething() {println("SuperClass is doing something.")}
}class Outer : SuperClass() {override fun doSomething() {println("Outer class is doing something.")}inner class Inner {fun callSuperClassMethod() {super@Outer.doSomething() }}
}fun main() {// SuperClass is doing something.Outer().Inner().callSuperClassMethod()
}
open class SuperClass {open fun doSomething() {println("SuperClass is doing something.")}
}class Outer : SuperClass() {override fun doSomething() {println("Outer class is doing something.")}inner class Inner {fun callSuperClassMethod() {doSomething() }}
}fun main() {// Outer class is doing something.Outer().Inner().callSuperClassMethod()
}

重写规则

实现继承受以下规则约束:如果一个类从其直接超类继承了同一成员的多个实现,那么它必须重写该成员并提供自己的实现(也许可以使用其中一个继承来的实现)。

为了指明从哪个超类型获取继承的实现,可以使用尖括号限定超类型名称的 super,例如 super<Base>

open class Rectangle {open fun draw() {}
}interface Polygon {// 接口成员默认是 open 的。fun draw() {}
}class Square() : Rectangle(), Polygon {// 编译器要求重写 draw()。override fun draw() {super<Rectangle>.draw() // 调用 Rectangle 的 draw() 方法。super<Polygon>.draw() // 调用 Polygon 的 draw() 方法。}
}

RectanglePolygon 继承是没问题的,但是它们都有各自的 draw() 实现,所以需要在 Square 类中重写 draw() 并提供一个单独的实现来消除歧义。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/895494.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sqlilabs--小实验

一、先盲注判断 ?id1 and sleep(2)-- 如果发现页面存在注点&#xff0c;使用时间盲注脚本进行注入 import requestsdef inject_database(url):name for i in range(1, 20): # 假设数据库名称长度不超过20low 48 # 0high 122 # zmiddle (low high) // 2while low &l…

【数字】异步FIFO面试的几个小问题与跨时钟域时序约束

入门数字设计的时候&#xff0c;跨时钟域的数据处理是绕不开的课题&#xff0c;特别是多比特数据跨时钟域时&#xff0c;都会采用异步FIFO的方法。 异步FIFO中涉及较多的考点这里记录几个以供大家参考。 1. 异步FIFO的空满判断分别在哪个域&#xff1f; 根据异步FIFO的结构&…

浅谈Java Spring Boot 框架分析和理解

Spring Boot是一个简化Spring开发的框架&#xff0c;它遵循“约定优于配置”的原则&#xff0c;通过内嵌的Tomcat、Jetty或Undertow等容器&#xff0c;使得开发者能够快速构建独立运行的、生产级别的基于Spring框架的应用程序。Spring Boot包含了大量的自动配置功能&#xff0c…

算法06-回溯算法

一、回溯算法详解 回溯算法是一种通过逐步构建解决方案来解决问题的算法。它通常用于解决组合问题、排列问题、子集问题等。回溯算法的核心思想是“试错”&#xff0c;即在每一步尝试所有可能的选项&#xff0c;如果发现当前选择无法达到目标&#xff0c;就回退到上一步&#…

RabbitMQ学习—day2—安装

目录 普通Linux安装 安装RabbitMQ 1、下载 2、安装 3. Web管理界面及授权操作 Docker 安装 强力推荐学docker&#xff0c;使用docker安装 普通Linux安装 安装RabbitMQ 1、下载 官网下载地址&#xff1a;https://www.rabbitmq.com/download.html(opens new window) 这…

降本增效 - VGF 构建轻量高性能日志管理平台

VFG 技术架构 Filebeat 接收Syslog &#xff0c;并进行日志分段&#xff0c;VictoriaLogs 持久化存储日志 &#xff0c;Grafana 可视化、数据查询、告警、数据导出。 为什么要用VictoriaLogs &#xff1f; 与Elasticsearch /Grafana Loki相比几十倍的CPU/内存/存储资源占用的…

初识camel智能体(一)

同目录下配置环境变量.env&#xff0c;内容如下&#xff0c; apikey从魔搭社区获取 QWEN_API_KEY4ff3ac8f-aebc******** 先上干货代码&#xff0c;主代码如下&#xff1a; from colorama import Forefrom camel.societies import RolePlaying from camel.utils import prin…

介绍 Liquibase、Flyway、Talend 和 Apache NiFi:选择适合的工具

在现代软件开发中&#xff0c;尤其是在数据库管理和数据集成方面&#xff0c;选择合适的工具至关重要。本文将介绍四个流行的工具&#xff1a;Liquibase、Flyway、Talend 和 Apache NiFi&#xff0c;分析它们的应用、依赖以及如何选择适合的工具。 1. Liquibase 简介&#xff…

Docker使用指南与Dockerfile文件详解:从入门到实战

Docker使用指南与Dockerfile文件详解:从入门到实战 文章目录 **Docker使用指南与Dockerfile文件详解:从入门到实战****引言****第一部分:Docker 核心概念速览****1. Docker 基础架构****2. Docker 核心命令****第二部分:Dockerfile 文件深度解析****1. Dockerfile 是什么?…

Qt工作总结03 <qSort按某一属性进行排序>

1. 代码样例 QList<QGraphicsTextItem *> Lst;qSort(Lst.begin(),Lst.end(),[](const QGraphicsTextItem *itemA,const QGraphicsTextItem *itemB) {return itemA->toPlainText().toDouble() < itemB->toPlainText().toDouble(); }); 2. 参考 QList 按结构体…

深度学习|表示学习|Instance Normalization 全面总结|26

如是我闻&#xff1a; 1. Instance Normalization&#xff08;IN&#xff09; Instance Normalization&#xff08;IN&#xff09;最早由 Ulyanov et al.&#xff08;2017&#xff09; 提出&#xff0c;主要用于 风格迁移&#xff08;Style Transfer&#xff09; 任务。它的核…

如何保持 mysql 和 redis 中数据的一致性?PegaDB 给出答案

MySQL 与 Redis 数据保持一致性是一个常见且复杂的问题&#xff0c;一般来说需要结合多种策略来平衡性能与一致性。 传统的解决策略是先读缓存&#xff0c;未命中则读数据库并回填缓存&#xff0c;但方式这种维护成本较高。 随着云数据库技术的发展&#xff0c;目前国内云厂商…

探索ELK 的魅力

在大数据时代&#xff0c;海量日志和数据的收集、存储、处理与可视化分析变得越来越重要。而 ELK 堆栈&#xff0c;由 Elasticsearch、Logstash、Beats 和 Kibana 组成&#xff0c;正是一个强大的开源解决方案&#xff0c;帮助开发者和运维人员高效管理和分析日志数据。本文将详…

用vue3写一个好看的wiki前端页面

以下是一个使用 Vue 3 Element Plus 实现的 Wiki 风格前端页面示例&#xff0c;包含现代设计、响应式布局和常用功能&#xff1a; <template><div class"wiki-container"><!-- 头部导航 --><el-header class"wiki-header"><d…

深度学习实战基础案例——卷积神经网络(CNN)基于DenseNet的眼疾检测|第4例

文章目录 前言一、数据准备二、项目实战2.1 设置GPU2.2 数据加载2.3 数据预处理2.4 数据划分2.5 搭建网络模型2.6 构建densenet1212.7 训练模型2.8 结果可视化 三、UI设计四、结果展示总结 前言 在当今社会&#xff0c;眼科疾病尤其是白内障对人们的视力健康构成了严重威胁。白…

DeepSeek的开源核爆:当技术民主化重构AI权力版图

2025年2月&#xff0c;全球AI产业正经历着由DeepSeek掀起的链式反应——这个首个开源千亿参数多模态模型的企业&#xff0c;用开放战略在技术壁垒森严的AI战场投下"制度性核弹"。其贡献不在于单纯的技术突破&#xff0c;而在于通过开源协议实现了三重维度的大爆炸&am…

代码随想录二叉树篇(含源码)

二叉树与递归 前言226.翻转二叉树算法思路及代码solution 1 用分解问题的思路来解决solution 2 用遍历的思路来解决 101.对称二叉树算法思路及代码solution 104.二叉树的最大深度算法思路及代码solution 1 遍历solution 2 分解问题 111.二叉树的最小深度算法思路及代码solution…

MyBatis映射文件 <resultMap> 元素详解与示例

引言 <resultMap> 是 MyBatis 中最核心的映射配置元素&#xff0c;用于解决数据库字段与 Java 对象属性之间的复杂映射问题&#xff0c;尤其是字段名不一致、嵌套对象关联、集合映射等场景。ResultMap 的设计思想是&#xff0c;对简单的语句做到零配置&#xff0c;对于复…

【xdoj离散数学上机】T283

递归函数易错&#xff1a; 防止出现递归死循环&#xff01; 题目 题目&#xff1a;求诱导出的等价关系的关系矩阵 问题描述 给定有限集合上二元关系的关系矩阵&#xff0c;求由其诱导出的等价关系的关系矩阵。 输入格式 第一行输入n&#xff0c;表示矩阵为n阶方阵&#xff0c…

WIN11上使用GraalVM打包springboot3项目为本地可执行文件exe

耐心肝才能成功 概念步骤概要详细步骤一. GraalVM 17二. 安装Visual Studio 2022三. 创建springboot四. IDEA最新版或者eclipse2025调试项目五. 打包exe 概念 springboot3生成的jar编译成windows本地C文件&#xff0c;不再依赖JVM运行 WINDOW编译较为复杂&#xff0c;限制条件…