本篇的知识点会在《 3.2.2 在 Kotlin 中实现 RecyclerView 及其点击事件》中被用到,到时可以回头进行查阅。
一、操作符重载
就像其他每种语言一样,Kotin有一些固定数量象征性的操作符,这里预定义了一些操作符执行一定的操作,我们可以在任何类中很容易地使用它们。比如典型的加(+),减(-),乘(),除(/)*,而且还有很多其它的。
类似Java这样的一些语言,这些操作符被限制在一些特定的数量类型上,且没有方法让其他类型数据使用这些操作符。不过Kotlin有这种方案:它有一些预定义的操作符,但是我们能够为任意数据类型而重载它们。方法也很简单,那就是创建一个函数,函数名为保留的操作符关键字,这样就可以让这个操作符的行为映射到这个函数,重载这些操作符可以增加代码可读性和简洁性。
1、操作符表
这里你可以看见一系列包括操作符和对应方法的表,对应方法必须在指定的类中通过各种可能性被实现:
一元操作符
操作符 | 函数 |
---|---|
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
a++ | a.inc() |
a-- | a.dec() |
二元操作符
操作符 | 函数 |
---|---|
a + b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.times(b) |
a / b | a.div(b) |
a % b | a.mod(b) |
a..b | a.rangeTo(b) |
a in b | a.contains(b) |
a !in b | !a.contains(b) |
a += b | a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timesAssign(b) |
a /= b | a.divAssign(b) |
a %= b | a.modAssign(b) |
数组操作符
操作符 | 函数 |
---|---|
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) |
等于操作符
操作符 | 函数 |
---|---|
a == b | a?.equals(b) ?: b === null |
a != b | !(a?.equals(b) ?: b === null) |
相等操作符有一点不同,为了达到正确合适的相等检查做了更复杂的转换,因为要得到一个确切的函数结构比较,不仅仅是指定的名称,方法必须要如下准确地被实现:
operator fun equals(other: Any?): Boolean
操作符===和!==用来做身份检查(它们分别是Java中的==和!=),并且它们不能被重载。
激活(invoking)函数调用
方法 | 调用 |
---|---|
a(i) | a.invoke(i) |
a(i, j) | a.invoke(i, j) |
a(i_1, ..., i_n) | a.invoke(i_1, ..., i_n) |
二、操作符重载的例子
1、onBindViewHolder中的使用
你可以想象,Kotlin List是实现了数组操作符的,所以我们可以像Java中的数组一样访问List的每一项。除此之外:在可修改的List中,每一项也可以用一个简单的方式被直接设置:
val x = myList[2]
myList[2] = 4
我们这里给出一个叫TestList的数据类,它是由很多其他额外的信息组成的,有趣的是我们可以直接访问它的每一项而不是请求内部的list得到某一项。做一个完全不相关的事情,我要去实现一个size()方法,它能稍微能简化一点当前的Adapter:
data class TestList(val city: String, val country: String, val dailyTest: List) {
operator fun get(position: Int): Test = dailyTest[position]
fun size(): Int = dailyTest.size
}
这样会使我们的onBindViewHolder更简单一点(后面的文章《 3.2.2 在 Kotlin 中实现 RecyclerView 及其点击事件》会继续讲解相关内容):
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
with(weekTest[position]) {
holder.textView.text = "$date - $description - $high/$low"
}
}
当然还有getItemCount()方法:
override fun getItemCount(): Int = weekTest.size()
2、扩展函数中的操作符
我们不需要去扩展我们自己的类,但是我需要去使用扩展函数扩展我们已经存在的类来让第三方的库能提供更多的操作。几个例子,我们可以去像访问List的方式去访问ViewGroup的view:
operator fun ViewGroup.get(position: Int): View = getChildAt(position)
现在真的可以非常简单地从一个ViewGroup中通过position得到一个view:
val container: ViewGroup = find(R.id.container)
val view = container[2]
如果你暂时看不懂其实没关系,这里我们所说的会在之后的章节中全部讲解到,点击关注即可。
三、可见性修饰符
和Java中的一样,Kotlin中的类、对象、接口、构造函数、属性以及它们的 setter 方法都可以有可见性修饰词,并且也是类似的四个:private、protected、internal 以及 public。但是Kotlin中这些修饰符是与我们Java中的使用是有些不同的,比如在这个语言中默认的修饰符是public,这就节约了我们很多的时间、字符和绳命,下面我们来详细看一下。
1、修饰符
(1)private
private 修饰符是我们使用的最具有限制性的修饰符,它表示它只能被自己所在的文件可见。所以如果我们给一个类声明为private,我们就不能在定义这个类之外的文件中使用它。
另一方面,如果我们在一个类里面使用了private修饰符,那访问权限就被限制在这个类里面了,甚至是继承这个类的子类也都不能使用它。所以类、对象、接口……(也就是包成员)如果被定义为private,那么它们只会对被定义所在的文件可见,如果被定义在了类或者接口中,那它们只对这个类或者接口可见。
(2)protected
这个修饰符的存在就是只能修饰类或者接口中的成员上,一个包成员不能被定义为protected,包级属性是不能修饰的。而定义在一个成员中,就与Java中的方式一样了:它可以被成员自己和继承它的成员可见,比如类和它的子类。
(3)internal
在Kotlin中就因为有了包级的属性(而java包下面只能是类或者接口等,所以Kotlin就可以很随意的去定义变量、类、接口、函数等),所以才多了个internal修饰符。而为了区别于protected,被internal修饰的类、函数或者接口可以在同一module下访问,除此之外如果internal修饰的是类内部的成员,那这个成员就成了只有这个类内部能访问的成员,比如private修饰的类,就只能在类的内部访问。
也就是说,如果是一个定义为internal的包成员的话,对所在的整个module可见。如果它是一个其它领域的成员,它就需要依赖那个领域的可见性了。比如,如果我们写了一个private类,那么它的internal修饰的函数的可见性就会限制与它所在的这个类的可见性。总之,我们可以访问同一个module中的internal修饰的类,但是不能访问其它module的。
什么是module?
根据Jetbrains的定义,一个module应该是一个单独的功能性的单位,它应该是可以被单独编译、运行、测试、debug的。根据我们项目不同的模块,可以在Android Studio中创建不同的module。在Eclipse中,这些module可以认为是在一个workspace中的不同的project。
(4)public
你应该可以猜想到,这是最没有限制的修饰符。而且这个修饰符与java最大的不同是,它在Kotlin中是默认的,也就是我们不声明的属性会默认成为public。当然了它只限制于它的领域,一个定义为public的成员被包含在一个private修饰的类中,这个成员在这个类以外也是不可见的。
2、构造器与代码润色
所有构造函数默认都是public的,它们类是可见的,可以被其它地方使用,不过我们也可以使用这个语法来把构造函数修改为private:
class C private constructor(a: Int) { ... }
如果我们在编写类的时候,你觉得某些属性因为是什么原因不能对别人可见,那就把它定义为private:
class RequestForecastCommand(private val zipCode: String)
上面这段代码所作的事情就是创建了一个不可修改的属性zipCode,它的值我们只能去得到,不能去修改它,所以这个不大的改动让代码看起来更加清晰。
而且在Kotlin中,我们不需要去指定一个函数的返回值类型,它可以让编译器推断出来。举个省略返回值类型的例子:
data class TestList(...) {
fun get(position: Int) = dailyTest[position]
fun size() = dailyTest.size()
}
我们可以省略返回值类型的典型情景是:当我们要给一个函数或者一个属性赋值,而又不需要去写代码块去实现的时候。
感谢优秀的你跋山涉水看到了这里,欢迎关注下让我们永远在一起!