kotlin基础——重载

重载算术运算符

重载二元算术运算

使用operator定义plus()方法后,可以直接使用+号求和

data class Point(val x: Int, val y: Int) {operator fun plus(other: Point): Point {return Point(x + other.x, y + other.y)}
}
val p1 = Point(1, 2)
val p2 = Point(3, 4)
print(p1 + p2)

若定义为扩展函数,也可实现

data class Point(val x: Int, val y: Int) operator fun Point.plus(other: Point): Point {return Point(x + other.x, y + other.y)
}

可供选择的重载运算有如下,不会自动支持交换性

在这里插入图片描述
不要求两个运算数是相同类型

data class Point(val x: Int, val y: Int)
operator fun Point.times(scale: Double): Point {return Point((x * scale).toInt(), (y * scale).toInt())
}
val p = Point(10, 20)
println(p * 1.5)

返回类型也可不同于任一运算数类型

operator fun Char.times(count: Int): String {return toString().repeat(count)
}
println('a' * 3)

Kotlin中的位运算符如下

  • shl一一带符号左移
  • shr一一带符号右移
  • ushr一一无符号右移
  • and一一与
  • or 一一或
  • xor一一异或
  • inv一一取反
println(0x0F and 0xF0)
println(0x0F or 0xF0)

重载复合赋值运算符

可通过定义plusAssign()重写+=

data class Point(var x: Int, var y: Int)
operator fun Point.plusAssign(other: Point): Unit {x += other.xy += other.y
}
val p = Point(1, 2)
p += Point(3, 4)
println(p)

系统自带函数库已经重写了该方法,将一个元素添加到可变集合

val number = ArrayList<Int>()
number += 1
number += 2
for (i in number) {println(i)
}

当使用+=时,plus()和plusAssign()都可能被调用,解决办法是

  • 替换运算符为普通函数调用
  • 用val代替var修饰变量,这样不能调用plusAssign()
  • 若类是不可变的,则提供plus()即可,若类是可变的则提供plusAssign()

重载一元运算符

如下获取负坐标

data class Point(var x: Int, var y: Int)
operator fun Point.unaryMinus(): Point {return Point(-x, -y)
}val p = Point(1, 2)
println(-p)

可重载的一元运算符有

在这里插入图片描述

重载比较运算符

等号运算符:equals

在Kotlin中使用==会被转换成equals,其可用于可空类型

a == b

相当于

a?.equal(b) ?: (b == null)

equals在Any类中且已经标记为operator,所以只需要用override复写,不能实现为扩展函数,因为继承自Any类的实现始终优先于扩展函数

class Point(var x: Int, var y: Int){override fun equals(other: Any?): Boolean {if (this === other) return trueif (javaClass != other?.javaClass) return falseother as Pointif (x != other.x) return falseif (y != other.y) return falsereturn true}override fun hashCode(): Int {var result = xresult = 31 * result + yreturn result}
}

排序运算符:compateTo

比较运算符 (<、> 、<= 和 >=)将转换成compateTo,Java中实现了Comparable的类在Kotlin中都可以使用运算符

class Person(val name: String, val age: Int) : Comparable<Person> {override fun compareTo(other: Person): Int {return compareValuesBy(this, other, Person::age, Person::name)}
}
val p1 = Person("A",1)
val p2 = Person("B",1)
println(p1 < p2)

如上按照字母表顺序比较,p1 < p2等价于p1.compateTo(p2) < 0,

集合和区间

下标运算符访问元素:get和set

data class Point(var x: Int, var y: Int)
operator fun Point.get(index: Int): Int {return when (index) {0 -> x1 -> yelse -> throw IndexOutOfBoundsException("Invalid coordinate")}
}
operator fun Point.set(index: Int, value: Int) {when (index) {0 -> x = value1 -> y = valueelse -> throw IndexOutOfBoundsException("Invalid coordinate")}
}val p = Point(1, 2)
println(p[0])
p[0] = 3
println(p[0])

in和contains

如下重写contains,使用in判断点是否在矩形中

data class Point(var x: Int, var y: Int)
data class Rectangle(val upperLeft: Point, val lowerRight: Point)operator fun Rectangle.contains(p: Point): Boolean {return p.x in upperLeft.x until lowerRight.x&& p.y in upperLeft.y until lowerRight.y
}val rect = Rectangle(Point(10, 20), Point(50, 50))
println(Point(20, 30) in rect)

…和rangeTo

使用1…10创建一个[1,10]的区间,可通过rangeTo为类定义该运算符,其是Comparable的扩展函数,若一个类实现了Comparable可直接使用rangeTo

val now = LocalDate.now()
val vacation = now..now.plusDays(10)
println(now.plusWeeks(1) in vacation)

rangeTo优先级低于算术运算符,使用时最好使用括号

val n = 9
println(0..(n + 1))(0..n).forEach { println(it) }

for和iterator

在for中使用in会调用iterator方法,如遍历String,其父类CharSequence已经实现了iterator

for (c in "abc") {println(c)
}

如下在LocalDate的闭区间上定义iterator

operator fun ClosedRange<LocalDate>.iterator(): Iterator<LocalDate> =object : Iterator<LocalDate> {var current = startoverride fun hasNext() =current <= endInclusiveoverride fun next() = current.apply {current = plusDays(1)}}val newYear = LocalDate.ofYearDay(2017, 1)
val dayOff = newYear.minusDays(1)..newYear
for (day in dayOff) {println(day)
}

解构声明

解构声明允许展开单个复合值,并使用它来初始化多个单独的变量,如下

data class Point(var x: Int, var y: Int)val p = Point(10, 20)
val (x, y) = p
println(x)
println(y)

解构声明初始化变量,相当于调用名为ComponentN的函数,N是声明中变量的位置,如下

class Point(var x: Int, var y: Int) {operator fun component1() = xoperator fun component2() = y
}

解构声明主要用于展开函数返回的数据类,如下将文件和扩展名分割

data class NameComponents(val name: String, val extension: String)fun splitFilename(fullName: String): NameComponents {val result = fullName.split(".", limit = 2)return NameComponents(result[0], result[1])
}
val (name, ext) = splitFilename("demo.kt")
println(name)
println(ext)

解构声明可以在数组和集合上使用,修改上面的splitFilename方法

fun splitFilename(fullName: String): NameComponents {val (name, ext) = fullName.split(".", limit = 2)return NameComponents(name, ext)
}

解构声明可用于循环中的变量声明,如遍历map

fun printEntries(map: Map<String, String>) {for ((key, value) in map) {println("$key -> $value")}
}
val map = mapOf("1" to "A", "2" to "B")
printEntries(map)

委托

by lazy

惰性初始化直到第一次访问该属性时才创建对象,常规的做法如下

class Email()fun loadEmails(person: Person): List<Email> {Thread.sleep(5000)return listOf(Email(), Email())
}

对于上述加载耗时的操作,使用_emails来存储,而另一个email用于返回

class Person(val name: String) {private var _emails: List<Email>? = nullval email: List<Email>get() {if (_emails == null) {_emails = loadEmails(this)}return _emails!!}
}

上面代码繁琐且非线程安全,使用by lazy可以让代码简单

class Person(val name: String) {val email by lazy { loadEmails(this) }
}

实现委托

实现一个例子,当对象改变时通知监听器,Java通过PropertyChangeSupport和PropertyChangeListener实现

open class PropertyChangeAware {protected val changeSupport = PropertyChangeSupport(this)fun addPropertyChangeListener(listener: PropertyChangeListener) {changeSupport.addPropertyChangeListener(listener)}fun removePropertyChangeListener(listener: PropertyChangeListener) {changeSupport.removePropertyChangeListener(listener)}
}

Person继承上面的PropertyChangeAware,在setter时通知listener

class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {var age: Int = ageset(newValue) {val oldValue = fieldfield = newValuechangeSupport.firePropertyChange("age", oldValue, newValue)}var salary: Int = salaryset(newValue) {val oldValue = fieldfield = newValuechangeSupport.firePropertyChange("salary", oldValue, newValue)}
}

Person添加listener,属性改变时响应

val p = Person("Tom", 20, 1000)
p.addPropertyChangeListener(PropertyChangeListener { event ->println("Property ${event.propertyName} change" +"from ${event.oldValue} to ${event.newValue}")}
)
p.age = 21

可以优化代码,将属性变化的值过程封装到ObservableProperty

class ObservableProperty(val proName: String,var propValue: Int,val changeSupport: PropertyChangeSupport
) {fun getValue(): Int = propValuefun setValue(newValue: Int) {val oldValue = propValuepropValue = newValuechangeSupport.firePropertyChange(proName, oldValue, newValue)}
}class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {val _age = ObservableProperty("age", age, changeSupport)var age: Intget() = _age.getValue()set(value) = _age.setValue(value)val _salary = ObservableProperty("age", age, changeSupport)var salary: Intget() = _salary.getValue()set(value) = _salary.setValue(value)
}

Kotlin中委托的原理也是如此,将属性的getter和setter委托给ObservableProperty,不同的是

  • setter和getter标记为operator,一个参数接受对象实例,用于读写属性,另一个用于表示属性本身,类型为KProperty
  • 使用by代替之前代码中调用setter和getter的步骤
class ObservableProperty(var propValue: Int,val changeSupport: PropertyChangeSupport
) {operator fun getValue(p: Person, prop: KProperty<*>): Int = propValueoperator fun setValue(p: Person, prop: KProperty<*>, newValue: Int) {val oldValue = propValuepropValue = newValuechangeSupport.firePropertyChange(prop.name, oldValue, newValue)}
}class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {var age: Int by ObservableProperty(age, changeSupport)var salary: Int by ObservableProperty(salary, changeSupport)
}

最后,Kotlin自带了Delegates.observable,代替我们写的ObservableProperty类,只需要传递一个Lambda通知属性值的修改

class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {private val observer = { prop: KProperty<*>, oldValue: Int, newValues: Int ->changeSupport.firePropertyChange(prop.name, oldValue, newValues)}var age: Int by Delegates.observable(age, observer)var salary: Int by Delegates.observable(salary, observer)
}

在这里插入图片描述

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

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

相关文章

修改选择框el-select样式,显示及下拉样式

修改选择框el-select样式,显示及下拉样式 .el-input__inner {background: rgba(25, 126, 195, 0.2);border: none;color: #fff; }.el-select-dropdown {background: rgba(19, 73, 104, 0.79);border: 2px solid #48e3ff;border-radius: 0; }.el-popper .popper__arrow {display…

java设计模式学习之【策略模式】

文章目录 引言策略模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用计算示例代码地址 引言 设想你正在玩一个策略游戏&#xff0c;每一个决策都会导致不同的游戏结局。同样地&#xff0c;在软件开发中&#xff0c;我们常常需要根据不同的场景或条件选择不同…

EOS链Ubuntu环境Install Prebuilt Binaries(安装预构建的二进制文件)的安装

[TOC](EOS链Ubuntu环境Install Prebuilt Binaries(安装预构建的二进制文件)的安装) EOS官网&#xff1a;https://eos.io/ 第一步 Ubuntu安装命令&#xff1a; 以下有两种安装方式&#xff0c;可以任选其一&#xff1a; 本文章已经上传绑定资源&#xff0c;也可以用命令安装。…

QT上位机开发(简易图像处理软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 大家都知道图像处理非常地重要&#xff0c;因为它不仅仅是可以用于拍照美颜&#xff0c;而且在工业、医疗和军事等方面也发挥着巨大的作用。另外一…

react-router-dom5升级到6

前言 升级前版本为5.1.2 下载与运行 下载 npm install react-router-dom6运行 运行发现报错: 将node_modules删除&#xff0c;重新执行npm i即可 运行发现如下报错 这是因为之前有引用react-router-dom.min&#xff0c;v6中取消了该文件&#xff0c;所以未找到文件导致报错。…

MO 2023 年度回顾

PART-ONE 行业态势 随着供需关系的变化&#xff0c;数据库的竞争在经历了 3 年 “百花齐放” 般的发展后&#xff0c;终于在 2023 年进入到了一个相对收拢的阶段。 2023 年&#xff0c;各个数据库厂商间很有默契地在两个方面达成了一致&#xff1a; HTAP 已经成为新一代数据…

前端下载文件问题之如何获取报错信息

问题&#xff1a;点击下载后。接口会生成并返回文件流。在极端情况下接口数据返回异常&#xff0c;需要抛出错误信息&#xff0c;比如后端拼接错误等情况、空文件情况。 难点&#xff1a;responseType设置为Blob后&#xff0c;返回内容为二进制文件流&#xff0c;从而无法获取错…

Linux_源码编译安装LAMP

1. 安装httpd服务 在配置 Apache 网站服务之前&#xff0c;需要正确安装好 httpd 服务器软件。httpd 服务器的安装可以选用 RPM 安装、源码编译安装这两种方式&#xff0c;前者相对比较简单、快速&#xff0c;但是在功能上存在一定的局限性。在实际的生产环境中&#xff0c;使…

堆排序算法

堆排序是利用堆这种数据结构而设计的一种排序算法&#xff0c;堆具有以下特点&#xff1a; 1.完全二叉树 2.二叉树每个结点的值都大于或等于其左右结点的值&#xff0c;称为大顶堆&#xff1b;或者每个结点的值都小于或等于其左右子结点的值&#xff0c;称为小顶堆 大顶堆 大…

马蹄集oj赛(双周赛第十八次)

目录 幸运的3 打靶 照亮街道 九次九日九重色 寻找串 竹鼠的白色季节 捉迷藏 好的三连 三角数 买马 可怜的小码哥 花园浇水 高次方程 幸运的3 难度:黄金时间限制: 1秒四占用内存:128M 你有 n 个数&#xff0c;可以将它们两两匹配(即将两数首尾相连)&#xff0c;每个…

基于Java+SpringBoot+vue+elementUI私人健身教练预约管理系统设计实现

基于JavaSpringBootvueelementUI私人健身教练预约管理系统设计实现 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目录 基于JavaSpringBootvueelementUI私人健身教练预约管理系统设计实现一、前言介绍&#xff1a;二、系统设计&#xff1a;2.1 性能需求分析2.2 B/S架构&…

Stable Diffusion汉化插件

今天为大家介绍Stable Diffusion的两种UI汉化包&#xff0c;一种是汉化包&#xff0c;就中文界面&#xff0c;方便大家对于繁杂的参数的模型的操作&#xff0c;一种是中英文对照界面&#xff0c;在中文提示下&#xff0c;同时显示英文&#xff0c;不但方便设置也同时学习了英文…

Vue3 自定义Hooks大全:一站式解决你的疑惑!

前言 不知道喜欢 vue3 的小伙伴和我是不是一样&#xff0c;刚上手vue3 的时候 对自定义hooks 一脸懵逼&#xff0c;在一些视频网站学习的时候老师讲解到自定义hooks 最喜欢用 加减乘除来描述 自定义hooks 是咋用的&#xff0c;可能是我理解能力比较差吧&#xff0c;我看了这个…

程序媛的mac修炼手册-- 终端shell的驾驭 zsh vs bash

进入终端(Terminal)为新下载的应用配置环境&#xff0c;是Mac生产力up up的关键一步&#xff0c;更是编程小白装大神的第一步。Fake it till you make it , 硅谷大神标准路径&#xff5e; shell的基本原理 为应用配置环境&#xff0c;相当于在应用和操作系统间架桥。由此&…

VSCode使用Remote SSH远程连接Windows 7

结论 VSCode Server不能启动&#xff0c;无法建立连接。 原因 .vscode-server 目录中的 node.exe 无法运行。 原因是Node.js仅在Windows 8.1、Windows Server 2012 R2或更高版本上受支持。 由于vscode基于node.js v14&#xff0c;不支持Windows 7操作系统。 另&#xff…

低成本高效率易部署,Ruff工业数采网关+IoT云平台赋能工厂数字化管理

随着工业4.0的快速发展&#xff0c;工业物联网是工业企业实现数字化转型的重要助力&#xff0c;物联网技术的应用也越来越广泛。 作为连接设备与网络的关键节点&#xff0c;数据采集网关是连接工业设备与物联网平台的硬件设备&#xff0c;它负责将工业设备的数据采集、传输到物…

Fast R-CNN

Fast R-CNN算法流程 对比与R-CNN其在第二步时并没有将所有的候选区域进行逐个的CNN特征提取&#xff0c;而是直接将整个图片进行一次CNN特征提取&#xff0c;让后再将候选区映射到feature map上。可想而知速度得到了提升。这里的ROI pooling层缩放到7x7就是将候选区域对应的特征…

企业使用人工智能情况调查

企业使用人工智能情况调查 人工智能在商业中的应用并不是什么新鲜事。多年来&#xff0c;公司一直在使用人工智能技术来削减成本并提高效率。 但最近生成式人工智能市场的激增帮助人工智能成为主流商业技术。具体来说&#xff0c;ChatGPT 和 Midjourney 等大型语言模型 (LLM)…

【Dart】=> [02] Dart初体验-基础语法(ing~

目录 Dart初体验基础语法变量常量数据类型数值类型 Dart初体验 效果&#xff1a;运行Dart程序&#xff0c;并输出字符串 ‘hello itcast’ 创建Dart文件 hello.dart&#xff0c;&#xff08;Dart文件的后缀是 .dart &#xff09;编写Dart代码 // 程序肯定都是有入口的 : main…

提取 PE 文件的各种信息

前段时间项目需要实现对 Windows PE 文件版本信息的提取&#xff0c;如文件说明、文件版本、产品名称、版权、原始文件名等信息。获取这些信息在 Windows 下当然有一系列的 API 函数供调用&#xff0c;简单方便。 我们先看一下PE文件结构&#xff0c;PE文件由DOS首部&#xff0…