约定的意义:就是让函数调用更加简洁。语法糖的一部分吧。
用一个是更简洁的符号调用,一个是特殊命名的函数。特殊命名是指Kotlin指定了和符号相对于的名字。
约定的方法都有一个关键字:operator。
举例:在类中定义了一个名为plus的方法,并且有operator关键字修饰,那么按照约定,你就可以在该类的实例上使用+运算符。
不知道有没有对你造成困扰。operator不是操作符重载的关键字吗?怎么又变成了什么“约定”的关键字了呢?
其实kotlin中的约定应用,包含以下几种,都用operator关键字定义。
1. 操作符号重载约定
2. 属性委托约定
3. 解构约定
4. invoke约定
5. Iterator约定
1. 操作符号重载约定
这个不再细讲了,有一点要注意,不像C++重载时重载函数的名字就是符号,但是kotlin是有映射表的。什么符号对应什么函数名字,比如,“+”对应的是“plus”。另外还有一些特殊一点的重载,比如,[]/get, contains
2. 属性委托约定
委托的关键词是by。by右边的对象,必须实现一个委托函数。这个函数用operator修饰。
委托函数需要返回一个初始化的对象。
具体详见上一篇:Kotlin学习笔记(八)by的作用,属性委托和类的委托,和Lazy的关系
3. 解构约定
可以把一个对象的属性值,一次性赋值给外面多个变量。
- fun test() {
- val point = Point(1,2)
- val(x,y) = point //此时x=1, y=2
- }
实现上面的效果,需要在Point类实现约定函数:
- public final operator fun component1(): Float{ return x }
-
- public final operator fun component2(): Float{ return y }
或者
把Point类声明称Data数据类,因为数据类在编译期,除了塞入:
equals()/hashCode() /toString() , 还会把属性按声明顺序对应一一声明componentN() 。
同时数据类必须满足以下要求:
如果说解构就上面举例那个作用,那就太莫名其妙了。一些有意义的作用在于:
- for ((key, value) in map) {
- // 直接使用该 key、value
-
- }
创建返回信息的数据类,在调用方法获取返回信息。如果使用解构声明将其分成不同的值:
- data class Result(val errorCode: Int, val message: Int,val result:String)
-
- fun getHttpResponse(): Result {
- return Result(resultCode, status,josnBody)
- }
-
- ------------------------------------------------------------------
- //获取返回值
- val(errorCode, message, result) = getHttpResponse()
和map遍历相识,就是将lambda中的Map.Entry参数进行解构声明:
- val map = mapOf(1 to 1)
- map.mapValues { (key, value) ->
- "key = $key ,value = $value "
- }
在lambda中,时常会看到_的使用。就是说这个参数,我现在不需要使用。这其实就是解构中的用法。如果需要时用下划线替代,要不然会得到不想要的属性值。
4.invoke约定
如果类使用operator声明了invoke(),则该类的对象就可以当做函数一样调用,即在变量后加上()。就相当于调用指定的方法了。
如果按照上面定义的意思,似乎也看不出来也有啥有意义的作用。
体现有意义关键在于,inovke函数可接受的参数类型也包括函数类型。
下面的代码:
- class TestInvoke() {
-
- operator fun invoke() {
- println("invoke()")
- }
-
- operator fun invoke(value: Int) {
- println("invoke value")
-
- }
-
- operator fun invoke(func: ()->Unit) {
- func()
- }
- }
-
- val test = TestInvoke()
- test()
- test(1)
- test{
- println("似乎比较有用的")
- }
函数类型其实就是实现了FunctionN接口的匿名类,然后当函数类型是函数类型时,这时传递给它一个lambda,lambda就会被编译成FunctionN的匿名内部类(当然是非内联的),然后调用lambda就变成了一次FunctionN接口的invoke调用。就像上面的例子一样。
- @SinceKotlin("1.3")
- interface FunctionN<out R> : Function<R>, FunctionBase<R> {
- /**
- * Invokes the function with the specified arguments.
- *
- * Must **throw exception** if the length of passed [args] is not equal to the parameter count returned by [arity].
- *
- * @param args arguments to the function
- */
- operator fun invoke(vararg args: Any?): R
-
- /**
- * Returns the number of arguments that must be passed to this function.
- */
- override val arity: Int
- }
5. Iterator约定
for循环中可以使用in运算符来表示执行迭代。这意味着Kotlin的for循环将被转换成list.iterator()的调用,然后反复调用hasNext 和 next 方法。
iterator方法也是Kotlin中的一种约定,这意味iterator()可以被定义为扩展函数。例如:Kotlin标准库中为Java的CharSequence定义了一个扩展函数iterator,使我们能遍历一个常规的Java字符串。
以上就是kotlin的约定。
引用: