区间
区间表达式由具有操作符形式 .. 的 rangeTo 函数辅以 in 和 !in 形成。区间是为任何可比较类型定义的,但对于整型原生类型,它有⼀个优化的实现。以下是使用区间的⼀些示例
fun main(args: Array<String>) {
for(i in 0..10){ //[0-10]的闭区间
print(i)
}
println()
for(i in 0 until 10){ // [0-10) 不包括10的半开区间
print(i)
}
println()
for(i in 0..10 step 2){ // 设置步长
print(i)
}
println()
for(i in 10 downTo 0){ //10 到 0 的倒数 包括0
print(i)
}
}
整型区间( IntRange 、LongRange 、CharRange )有⼀个额外的特性:它们可以迭代。编译器负责将其转换为类似 Java 的基于索引的 for-循环而无额外开销。
fun main(args: Array<String>) {
var intRange = IntRange(0,10)
for (i in intRange){
print(i)
}
println()
var charRange = CharRange('a','z')
for (c in charRange step 2){
print(c)
}
println()
var longRange = LongRange(1,10L)
for (l in longRange){
var s = StringBuilder("").append(l)
}
}
它是如何⼯作的
区间实现了该库中的⼀个公共接口:ClosedRange<T>
。
ClosedRange<T>
在数学意义上表示⼀个闭区间,它是为可比较类型定义的。它有两个端点:start 和 endInclusive 他们都包含在区间内。其主要操作是 contains
,通常以 in/!in
操作符形式使⽤。
整型数列( IntProgression 、LongProgression 、CharProgression )
表示等差数列。数列由 first 元素、last 元素和非零的 step 定义。
第⼀个元素是 first ,后续元素是前⼀个元素加上 step
。last
元素总会被迭代命中,除非该数列是空的。
数列是 Iterable<N>
的⼦类型,其中 N
分别为 Int 、Long
或者 Char
,所以它可⽤于 for-
循环以及像 map 、filter
等函数中。对Progression
迭代相当于 Java/JavaScript
的基于索引的 for-
循环:
for (int i = first; i != last; i += step) {
// ……
}
对于整型类型,.. 操作符创建⼀个同时实现 ClosedRange<T>
和 *Progression
的对象。例如,IntRange
实现了 ClosedRange<Int>
并扩展⾃ IntProgression
,因此为 IntProgression
定义的所有操作也可用于 IntRange
。downTo()
和 step()
函数的结果总是⼀个*Progression
。
数列由在其伴生对象中定义的 fromClosedRange
函数构造:
fun main(args: Array<String>) {
var intP = IntProgression.fromClosedRange(0,10,2)
for (i in intP){
println(i)
}
var intP2 = IntProgression.fromClosedRange(0,10,2)
for (i in intP2.reversed()){ //倒叙
println(i)
}
}
数列的 last
元素这样计算:对于正的 step
找到不大于 end
值的最大值、或者对于负的 step
找到不小于 end
值的最小值,使得 (last -first) % increment == 0
。
一些使用的函数:
rangeTo()
整型类型的 rangeTo()
操作符只是调用*Range
类的构造函数,例如:
class Int {
//……
operator fun rangeTo(other: Long): LongRange = LongRange(this, other)
//……
operator fun rangeTo(other: Int): IntRange = IntRange(this, other)
//……
}
fun main(args: Array<String>) {
var intRange = 1.rangeTo(10) //这个函数不能中缀表示
for(i in intRange){
println(i)
}
}
downTo()
扩展函数 downTo()
是为任何整型类型对定义的,这⾥有两个例⼦:
fun Long.downTo(other: Int): LongProgression {
return LongProgression.fromClosedRange(this, other.toLong(), -1L)
}
fun Byte.downTo(other: Int): IntProgression {
return IntProgression.fromClosedRange(this.toInt(), other, -1)
}
fun main(args: Array<String>) {
var intRange = 10.downTo(0)
for (i in intRange){
println(i)
}
}
reversed()
扩展函数 reversed()
是为每个 *Progression
类定义的,并且所有这些函数返回反转后的数列。
fun IntProgression.reversed(): IntProgression {
return IntProgression.fromClosedRange(last, first, -step)
}
fun main(args: Array<String>) {
var intRange = 10.downTo(0)
for (i in intRange){
println(i)
}
var intRange1 = intRange.reversed() //会把步长变为相反数
for (i in intRange1){
println(i)
}
}
step()
扩展函数 step()
是为每个 *Progression
类定义的,所有这些函数都返回带有修改了 step
值(函数参数)的数列。步长(step)
值必须始终为正,因此该函数不会更改迭代的方向。
fun IntProgression.step(step: Int): IntProgression {
if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step")
return IntProgression.fromClosedRange(first, last, if (this.step > 0) step else -step)
}
fun CharProgression.step(step: Int): CharProgression {
if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step")
return CharProgression.fromClosedRange(first, last, if (this.step > 0) step else -step)
}
fun main(args: Array<String>) {
var intRange = 10.downTo(0)
for (i in intRange){
println(i)
}
var intRange1 = intRange.step(2) //设置步长
for (i in intRange1){
println(i)
}
}
浮点数的rangTo
fun main(args: Array<String>) {
var range1 = (1.45).rangeTo(2.36)
println(6.0 in range1) //浮点数不能用于迭代
}
类型的检查与转换“is”与“as”
is 与 !is 操作符
我们可以在运⾏时通过使⽤ is
操作符或其否定形式 !is
来检查对象是否符合给定类型:
fun <T> isInt(obj : T) = obj is Int
fun main(args: Array<String>) {
println(isInt(1.0))//输出false
}
智能转换
在许多情况下,不需要在 Kotlin 中使用显式转换操作符,因为编译器跟踪不可变值的 is -
检查,并在需要时自动插⼊(安全的)转换:
fun <T> isString(obj : T){
if (obj is String){
println(obj.length) //这里会智能转换为String类型
}else{
println("obj 不是String类型")
}
}
编译器⾜够聪明,能够知道如果反向检查导致返回那么该转换是安全的:
fun <T> isString1(obj: T){
if (obj !is String) return
println(obj.length) // x ⾃动转换为字符串
}
fun main(args: Array<String>) {
isString("lina")
isString(1)
}
在 && 和 || 的右侧,也能自动转换为安全的类型
fun <T>isString2(obj :T){
if (obj is String && obj.length != 0) {
println("obj is a String")
}else{
println("obj is not a String")
}
}
这些智能转换,对于when
和while
也一样
fun <T>typeOfObj(obj: T)= when(obj){
is String -> "String"
is Int -> "Int"
else -> "other"
}
fun main(args: Array<String>) {
println(typeOfObj(4.0))
}
请注意,当编译器不能保证变量在检查和使用之间不可改变时,智能转换不能用。更具体地,智能转换能否适用根据以下规则:
1. val 局部变量⸺总是可以;
2. val 属性⸺如果属性是 private 或 internal,或者该检查在声明属性的同⼀模块中执行。智能转换不适用于 open 的属性或者具有自定义 getter 的属性;
3. var 局部变量⸺如果变量在检查和使用之间没有修改、并且没有在会修改它的 lambda 中捕获;
4. var 属性⸺决不可能(因为该变量可以随时被其他代码修改)。
“不安全的”转换操作符
通常,如果转换是不可能的,转换操作符会抛出⼀个异常。因此,我们称之为不安全的。Kotlin 中的不安全转换由中缀操作符 as
完成
fun asTest(){
var x : Int = 1
var y:String = x as String //会抛出java.lang.ClassCastException
}
fun main(args: Array<String>) {
asTest()
}
fun asTest(){
var x : Int? = 1
var y : String? = x as String? //但是这仍然不是安全的,会抛出java.lang.ClassCastException,因为x不是空
println(y)
}
fun main(args: Array<String>) {
asTest()
}
fun asTest(){
var x : Int? = null
var y : String? = x as String? //但是安全的,会抛出java.lang.ClassCastException,因为x为null
//但是实际应用中几乎可能这样
println(y)
}
fun main(args: Array<String>) {
asTest()
}
“安全的”( 可空)转换操作符
为了避免抛出异常,可以使⽤安全转换操作符 as?
,它可以在失败时返回 null
fun asTest(){
var x : Int? = 1
var y : String? = x as String? //不安全,会抛异常
println(y)
//上面的代码换成下面的就会是安全的了,因为转换失败的时候会返回一个null
var x1 : Int = 1
var y1: String? = x1 as? String //转换不成功会返回null,而不会跑异常。
println(y1)
}
fun main(args: Array<String>) {
asTest()
}
请注意,尽管事实上 as? 的右边是⼀个非空类型的 String ,但是其转换的结果是可空的。
而且,接受as?表达式的一定要是可空的
this表达式
为了表示当前的 接收者 我们使用 this 表达式:
1. 在类的成员中,this 指的是该类的当前对象;
2. 在扩展函数或者带接收者的函数字面值中,this 表示在点左侧传递的 接收者 参数。
如果 this 没有限定符,它指的是最内层的包含它的作用域。要引用其他作用域中的 this,请使用 标签限定符:
限定的this
要访问来自外部作用域的this(⼀个类 或者扩展函数,或者带标签的带接收者的函数字面值)我们使用 this@label
,其中 @label
是⼀个代指 this
来源的标签:
class A { // 隐式标签 @A
inner class B { // 隐式标签 @B
fun Int.foo() { // 隐式标签 @foo
val a = this@A // A 的 this
val b = this@B // B 的 this
val c = this // foo() 的接收者,⼀个 Int
val c1 = this@foo // foo() 的接收者,⼀个 Int
val funLit = lambda@ fun String.() {
val d = this // funLit 的接收者 String类的对象
}
val funLit2 = { s: String ->
// foo() 的接收者,因为它包含的 lambda 表达式
// 没有任何接收者
val d1 = this
}
}
}
}
fun main(args: Array<String>) {
var funList = lambda@ fun String.(){ //这里相当于为String类扩展了一个funList方法
val d = this //接受者是funList的接受者,所以this是String的
println(d)
}
var s = "hello"
s.funList()
}