Kotlin浅谈(二)

image.png

原作者:移动研发部-郭烽
文章转载自公众号:轻易科技技术中心
原文地址:https://mp.weixin.qq.com/s/pUGh2fb-5Qy2yVgpxo3Wtg

在上篇浅谈Kotlin,Java的终结者?(一)中介绍了kotlin的基本数据类型、类和对象等,本篇将继续讲解kotlin中的继承、接口、条件控制、循环控制等基础语法。

Kotlin基础语法

5、继承

Kotlin 中所有类都继承该 Any 类,它是所有类的超类,对于没有超类型声明的类是默认超类

class  Example  // 从 Any 隐式继承

Any 默认提供了三个函数:

equals() 
hashCode() 
toString() 

注意:Any 不是 java.lang.Object。

如果一个类要被继承,可以使用 open 关键字进行修饰。

open class Base(p: Int) // 定义基类
class Derived(p: Int) : Base(p) 

重写

在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词:

/**用户基类**/
open class Person {
    open fun study() {       // 允许子类重写
        println("我在学习计算机")
    }
}
/**子类继承 Person 类**/
class Student : Person() {
    override fun study() {    // 重写方法
        println("我在学习kotlin")
    }
}
fun main(args: Array) {
    val s = Student()
    s.study()
}

输出结果为:

我在学习kotlin

如果有多个相同的方法(继承或者实现自其他类,如A、B类),则必须要重写该方法,使用super范型去选择性地调用父类的实现。


open class A {
    open fun f() {
        print("A")
    }
    fun a() {
        print("a")
    }
}
interface B {
    fun f() {
        print("B")
    } //接口的成员变量默认是 open 的
    fun b() {
        print("b")
    }
}
class C() : A(), B {
    override fun f() {
        super.f()//调用 A.f()
        super.f()//调用 B.f()
    }
}
fun main(args: Array) {
    val c = C()
    c.f()
}

C 继承自 a() 或 b(), C 不仅可以从 A 或则 B 中继承函数,而且 C 可以继承 A()、B() 中共有的函数。此时该函数在中只有一个实现,为了消除歧义,该函数必须调用A()和B()中该函数的实现,并提供自己的实现。

输出结果为:

AB
属性重写

属性重写使用 override 关键字,属性必须具有兼容类型,每一个声明的属性都可以通过初始化程序或者getter方法被重写:

open class Foo {
    open val x: Int
        get{
            ……
        }
}
class Bar1 : Foo() {
    override val x: Int = ……
}

你可以用一个var属性重写一个val属性,但是反过来不行。因为val属性本身定义了getter方法,重写为var属性会在衍生类中额外声明一个setter方法

你可以在主构造函数中使用 override 关键字作为属性声明的一部分:

interface Foo {
    val count: Int
}
class Bar1(override val count: Int) : Foo

class Bar2 : Foo {
    override var count: Int = 0
}
6、接口

Kotlin 接口与 Java 8 类似,使用 interface 关键字定义接口,允许方法有默认实现:

interface MyInterface {
    fun bar()    // 未实现
    fun foo() {  //已实现
        // 可选的方法体
        println("foo")
    }
}

实现接口

一个类或者对象可以实现一个或多个接口。

class Child : MyInterface {
    override fun bar() {
        // 方法体
    }
}
实例
interface MyInterface { fun bar() fun foo() { // 可选的方法体 println("foo") }
} class Child : MyInterface { override fun bar() { // 方法体 println("bar") }
} fun main(args: Array) { val c = Child() c.foo() c.bar() } 

输出结果为:

foo
bar

接口中的属性

接口中的属性只能是抽象的,不允许初始化值,接口不会保存属性值,实现接口时,必须重写属性:

interface MyInterface { var name: String //name 属性, 抽象的 } class MyImpl : MyInterface { override var name: String = "runoob" //重写属性 } 
实例
interface MyInterface {
    fun bar()
    fun foo() {
        // 可选的方法体
        println("foo")
    }
}
class Child : MyInterface {
    override fun bar() {
        // 方法体
        println("bar")
    }
}
fun main(args: Array) {
    val c = Child()
    c.foo()
    c.bar()
}

输出结果为:

foo
bar
runoob

函数的重写

实现多个接口时,可能会遇到同一方法继承多个实现的问题。例如:

实例
interface B {
    fun foo() {
        print("B")
    }   // 已实现
    fun bar() {
        print("bar")
    } // 已实现
}
class C : A {
    override fun bar() {
        print("bar")
    }   // 重写
}
class D : A, B {
    override fun foo() {
        super.foo()
        super.foo()
    }
    override fun bar() {
        super.bar()
    }
}
fun main(args: Array) {
    val d = D()
    d.foo()
    d.bar()
}

输出结果为:

ABbar

实例中接口 A 和 B 都定义了方法 foo() 和 bar(), 两者都实现了 foo(), B 实现了 bar()。因为 C 是一个实现了 A 的具体类,所以必须要重写 bar() 并实现这个抽象方法。

然而,如果我们从 A 和 B 派生 D,我们需要实现多个接口继承的所有方法,并指明 D 应该如何实现它们。这一规则 既适用于继承单个实现(bar())的方法也适用于继承多个实现(foo())的方法。

7、条件控制

IF表达式

一个 if 语句包含一个布尔表达式和一条或多条语句。

// 传统用法
var max = a
if (a < b) max = b

// 使用 else
var max: Int
if (a > b)
{
    max = a
} else
{
    max = b
}
// 作为表达式
val max = if (a > b) a else b

我们也可以把 IF 表达式的结果赋值给一个变量。

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}

这也说明我也不需要像Java那种有一个三元操作符,因为我们可以使用它来简单实现:

val c = if (condition) a else b
实例
fun main(args: Array) {
    var x = 0
    if (x > 0) {
        println("x 大于 0")
    } else if (x == 0) {
        println("x 等于 0")
    } else {
        println("x 小于 0")
    }
    var a = 1
    var b = 2
    val c = if (a >= b) a else b
    println("c 的值为 $c")
}

输出结果为:

x 等于  0
 c 的值为  2

使用区间

使用 in 运算符来检测某个数字是否在指定区间内,区间格式为 x..y :

实例
fun main(args: Array) {
    val x = 5
    val y = 9
    if (x in 1..8) {
        println("x 在区间内")
    }
}

输出结果为:

x 在区间内

when表达式

when 将它的参数和所有的分支条件顺序比较,直到某个分支满足条件。

when 既可以被当做表达式使用也可以被当做语句使用。如果它被当做表达式,符合条件的分支的值就是整个表达式的值,如果当做语句使用, 则忽略个别分支的值。

when 类似其他语言的 switch 操作符。其最简单的形式如下:

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> {
        // 注意这个块
        print("x 不是 1 ,也不是 2")
    }
}

在 when 中,else 同 switch 的 default。如果其他分支都不满足条件将会求值 else 分支。

如果很多分支需要用相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔:

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

我们也可以检测一个值在(in)或者不在(!in)一个区间或者集合中:

when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

另一种可能性是检测一个值是(is)或者不是(!is)一个特定类型的值。注意:由于智能转换,你可以访问该类型的方法和属性而无需 任何额外的检测。

fun hasPrefix(x: Any) = when (x) {
    is String -> x.startsWith("prefix")
    else -> false
}

when 也可以用来取代 if-else if链。如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:

when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}
实例
fun main(args: Array) {
    var x = 0
    when (x) {
        0, 1 -> println("x == 0 or x == 1")
        else -> println("otherwise")
    }
    when (x) {
        1 -> println("x == 1")
        2 -> println("x == 2")
        else -> { // 注意这个块
            println("x 不是 1 ,也不是 2")
        }
    }
    when (x) {
        in 0..10 -> println("x 在该区间范围内")
        else -> println("x 不在该区间范围内")
    }
}

输出结果:

x ==  0  or x ==  1 
x 不是  1  ,也不是  2
 x 在该区间范围内

when 中使用 in 运算符来判断集合内是否包含某实例:

fun main(args: Array) {
    val items = setOf("apple", "banana", "kiwi")
    when {
        "orange" in items -> println("juicy")
        "apple" in items -> println("apple is fine too")
    }
} 

输出结果:

apple is fine too
8、循环控制

For循环

for 循环可以对任何提供迭代器(iterator)的对象进行遍历,语法如下:

for (item in collection) print(item)

循环体可以是一个代码块:

for (item: Int in ints) { // …… } 

如上所述,for 可以循环遍历任何提供了迭代器的对象。

如果你想要通过索引遍历一个数组或者一个 list,你可以这么做:

for (i in array.indices) { print(array[i]) } 

注意这种"在区间上遍历"会编译成优化的实现而不会创建额外对象。

或者你可以用库函数 withIndex:

for ((index, value) in array.withIndex()) { println("the element at $index is $value") } 
实例

对集合进行迭代:

fun main(args: Array) {
    val items = listOf("apple", "banana", "kiwi")
    for (item in items) {
        println(item)
    }
    for (index in items.indices) {
        println("item at $index is ${items[index]}")
    }
}

输出结果:

apple
banana
kiwi
item at 0  is apple
item at 1  is banana
item at 2  is kiwi

while与do...while循环

while是最基本的循环,它的结构为:

while( 布尔表达式 ) { //循环内容 } 

do…while 循环 对于 while 语句而言,如果不满足条件,则不能进入循环。但有时候我们需要即使不满足条件,也至少执行一次。

do…while 循环和 while 循环相似,不同的是,do…while 循环至少会执行一次。

do { //代码语句 }while(布尔表达式);
实例
fun main(args: Array) {
    println("----while 使用-----")
    var x = 5
    while (x > 0) {
        println(x--)
    }
    println("----do...while 使用-----")
    var y = 5
    do {
        println(y--)
    } while (y > 0)
}

输出结果:

54321----do...while 使用-----54321

返回和跳转

Kotlin 有三种结构化跳转表达式:

  • return。默认从最直接包围它的函数或者匿名函数返回。

  • break。终止最直接包围它的循环。

  • continue。继续下一次最直接包围它的循环。

在循环中 Kotlin 支持传统的 break 和 continue 操作符。

fun main(args: Array) {
  for (i in 1..10) {
  // i 为 3 时跳过当前循环,继续下一次循环
  if (i == 3) continue
  println(i)
  // i 为 6 时 跳出循环
  if (i > 5) break
  } } 

输出结果:

12456

Break和Continue标签

在 Kotlin 中任何表达式都可以用标签(label)来标记。标签的格式为标识符后跟 @ 符号,例如:abc@、fooBar@都是有效的标签。要为一个表达式加标签,我们只要在其前加标签即可。

loop@ for (i in 1..100){
  // …… } 

现在,我们可以用标签限制 break 或者continue:

loop@ for (i in 1..100) {
  for (j in 1..100) {
  if (……) break@loop
    } } 

标签限制的 break 跳转到刚好位于该标签指定的循环后面的执行点。continue 继续标签指定的循环的下一次迭代。

标签处返回

Kotlin 有函数字面量、局部函数和对象表达式。因此 Kotlin 的函数可以被嵌套。标签限制的 return 允许我们从外层函数返回。最重要的一个用途就是从 lambda 表达式中返回。回想一下我们这么写的时候:

fun foo() {
  ints.forEach {
  if (it == 0) return
  print(it)
  } } 

这个 return 表达式从最直接包围它的函数即 foo 中返回。(注意,这种非局部的返回只支持传给内联函数的 lambda 表达式。) 如果我们需要从 lambda 表达式中返回,我们必须给它加标签并用以限制 return。

fun foo() {
  ints.forEach lit@ {
  if (it == 0) return@lit
  print(it)
  } } 

现在,它只会从 lambda 表达式中返回。通常情况下使用隐式标签更方便。该标签与接受该 lambda 的函数同名。

fun foo() {
  ints.forEach {
  if (it == 0) return@forEach
  print(it)
  } } 

或者,我们用一个匿名函数替代 lambda 表达式。匿名函数内部的 return 语句将从该匿名函数自身返回

fun foo() {
  ints.forEach(fun(value: Int) {
  if (value == 0) return
  print(value)
  }) } 

当要返一个回值的时候,解析器优先选用标签限制的 return,即

return@a 1

意为"从标签 @a 返回 1",而不是"返回一个标签标注的表达式 (@a 1)"。

总结:

kotlin中interface、if语句、for、while等语句与java基本相同,尤其是功能强大的when表达式,拥有区间判断、switch、contains等多种特性,相对Java来说,代码更加简洁高效、支持lambda表达式等。return、break、Continue用法则与java基本一致,还可使用注解标签等多种api。

后续将继续发布探究kotlin的基础语法与编写规范等相关方面的文章,欢迎大家一起学习、讨论。

你可能感兴趣的:(Kotlin浅谈(二))