Kotlin编码窍门之操作符重载(Operator overloading)

Kotlin允许我们为我们的类型的预定义的操作符集提供实现。这些操作符有固定的标识(如+或*)和固定的优先级。为了实现这样的操作符,我们需要为目标类型(即双目操作符左侧的类型和一元操作符的参数类型)提供一个具有固定名称的成员函数或扩展函数。重载操作符的函数需要使用operator修饰符标记。

此外,我们还描述了规范不同运算符运算符重载的约定。

一元操作(Unary operations)

一元前缀操作符(Unary prefix operators)

Expression Translated to
+a a.unaryPlus()
-a a.unaryMinus()
!a a.not()

上表是说,当编译器处理如+a的表达式时,它将执行以下步骤:

  1. 确定a的类型,令其为T
  2. 对接收者T查找被operator修饰的无参函数unaryPlus(),即成员函数或扩展函数
  3. 如果函数不存在或不明确,则导致编译错误
  4. 如果函数存在且返回类型为R,则表达式+a具有类型R

注意,这些操及所有其他操作都针对基本类型做了优化,不会为它们引入函数调用的开销。

如下示例如何重载一元减运算符:

data class Point(val x: Int, val y: Int)

operator fun Point.unaryMinus() = Point(-x, -y)

val point = Point(10, 20)
println(-point)  // prints "(-10, -20)"

递增和递减(Increments and decrements)

Expression Translated to
a++ a.inc()+ see below()
a-- a.dex()+ see below()

函数inc()和dec()必须返回一个值,将分配给使用++-- 操作的变量。

当编译器解析后缀操作符,如a++的时候,将会执行以下步骤:

  1. 确定a的类型,令其为T
  2. 查找一个接收者为类型T的,带有operator操作符修饰的无参函数inc()
  3. 检查函数的返回值类型是否是T的子类

计算表达是的步骤是:

  1. a的值存储到临时存储a0
  2. a.inc()的结果赋值给a
  3. a0作为表达式的结果返回

对于a--,步骤是完全类似的。

对于前缀形式的++a和--a,其步骤是:

  1. a.inc()结果赋值给a
  2. a的新值作为表达式结果返回

双目操作符(Binary operations)

算术运算符(Arithmetic operators)

Expression Translated to
a+b a.plus(b)
a-b a.minus(b)
a*b a.times(b)
a/b a.div(b)
a%b a.re(b),a.mod(b)后者已过时
a..b a.rangeTo(b)

对于上表中的操作符,编译器只是对应解析为列中的表达式。

注意:Kotlin1.1开始支持rem操作,mod操作在Kotlin1.1中被弃用。

例子

下面是一个从给定值开始的计数器的例子,它可以使用+运算符来增加计数:

data class Counter(var dayIndex: Int) {
    operator fun plus(increment: Int): Counter {
        return Counter(dayIndex + increment)
    }
}

in操作符('In' operator)

Expression Translated to
a in b b.contains(a)
a !in b !b.contains(a)

操作符in!in的步骤是相同的,但参数的顺序是相反的。

索引访问操作符(Indexed access operator)

Expression Translated to
a[i] a.get(i)
a[i,j] a.get(i,j)
a[i_1,...,i_n] a.get(i_1,...,i_n)
a[i] = b a.set(i,b)
a[i,j] = b a.set(i,j,b)
a[i_1,...,i_n] = b a.set(i_1,...,i_n,b)

方括号转换为调用带有适当数量参数的getset函数。

调用操作符(Invoke operator)

Expression Translated to
a() a.invoke()
a(i) a.invoke(i)
a(i,j) a.invoke(i,j)
a(i_1,...,i_n) a.invoke(i_1,...,i_n)

圆括号转换为调用带有适当数量参数的invoke

广义赋值,运算赋值符(Augmented assignments)

Expression Translated to
a += b a.plusAssign(b)
a -= b a.minusAssign(b)
a *= b a.timesAssign(b)
a /= b a.divAssgin(b)
a %= b a.modAssign(b)

对于赋值操作,如a += b,编译器将执行以下步骤:

  1. 如果右列的函数可用
    • 如果相应的二元算术函数也可用,将报错(模糊)
    • 确保其返回值类型是Unit,否则报错
    • 生成a.plusAssign(b)的代码
  2. 否则,尝试生成a = a + b的代码(这里包含类型检查:a + b的类型必须是a的子类型)

注意:在Kotlin中,赋值不是表达式(即整体不能代表一个值)

相等和不等操作符(Equality and inequality operators)

Expression Translated to
a == b a?.equals(b) ?: (b === null)
a != b !(a?.equals(b) ?: (b === null))

注意:===!==(同一性检查)不可重载,因此不存在对它们的约定。

操作符==比较特殊,它被译为一个屏蔽null的复杂的表达式。null==null总是true,对于非nullxx==null总是false,且不会再调用x.equals().

比较操作符(Comparison operators)

Expression Translated to
a > b a.compareTo(b)>0
a < b a.compareTo(b)<0
a >= b a.compareTo(b) >=0
a <= b a.compareTo(b) <=0

所有的比较都转换为compareTo函数的调用,该函数需要返回Int值。

命名函数的中缀调用(Infix calls for named functions)

通过中缀函数的调用,可以模拟自定义中缀操作符。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • C++运算符重载-上篇 本章内容:1. 运算符重载的概述2. 重载算术运算符3. 重载按位运算符和二元逻辑运算符4...
    Haley_2013阅读 2,278评论 0 51
  • C++运算符重载-下篇 本章内容:1. 运算符重载的概述2. 重载算术运算符3. 重载按位运算符和二元逻辑运算符4...
    Haley_2013阅读 1,430评论 0 49
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,134评论 9 118
  • 2017年6月13日,晴有风,凉爽 一个人呱呱坠地,好奇的睁眼第一次看到花花绿绿的世界,迈出人生...
    cf302fb8f796阅读 236评论 0 0
  • 当你幻想, 身边的陌生姑娘,在等待着约好的未来人!不用疑虑,大胆上前,你只是想和她认识一下,也许她等的人是你...
    忠善阅读 223评论 0 0