kotlin学习(二十一)—— 区间 ,类型转换符,this表达式

区间

区间表达式由具有操作符形式 .. 的 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 ,后续元素是前⼀个元素加上 steplast 元素总会被迭代命中,除非该数列是空的。

数列是 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 定义的所有操作也可用于 IntRangedownTo()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")
    }
}

这些智能转换,对于whenwhile也一样

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()
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值