十二, kotlin控制语句
1, if else 语句
// 传统用法 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 //类似于java中的三元运算符, kotlin中没有三元运算符
// 我们也可以把 IF 表达式的结果赋值给一个变量。
val max = if (a > b) { print("Choose a") a } else { print("Choose b") b } 使用区间 使用 in 运算符来检测某个数字是否在指定区间内,区间格式为 x..y : val x = 5 val y = 9 if (x in 1..8) { println("x 在区间内") }
2, When 表达式 类似于Java中的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 类似于Java中的instanceof 用来判断数据类型 和java不同的是, 使用is判断过类型后, 不用再强转数据类型, // 可以智能转换 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") }
3, for循环, for 可以循环遍历任何提供了迭代器的对象。 数组, 区间(0..10), 集合, Map等
语法:
for (item in collection) { print(item) }
通过索引遍历数组
for (i in array.indices) { print(array[i]) }
对集合进行遍历
val items = listOf("apple", "banana", "kiwi") for (item in items) { println(item) } for (index in items.indices) { println("item at $index is ${items[index]}") }
正常循环 1..4 或者1 until 5(不包含5)
for (i in 1..4) print(i) // 打印结果为: "1234"
反序循环 4 downTo 1 包含头和尾
for (i in 4 downTo 1) print(i) // 打印结果为: "4321"
也支持指定步长:关键字:step
for (i in 1..4 step 2) print(i) // 打印结果为: "13" for (i in 4 downTo 1 step 2) print(i) // 打印结果为: "42"
4, while 与 do...while 循环, return, break,contine 和java一样,
标签处返回, 稍有区别
fun foo() {//显式标签 ints.forEach lit@ { if (it == 0) return@lit print(it) } }
fun foo() { ints.forEach {//隐式标签 if (it == 0) return@forEach print(it) } }
十三, 类和对象
1, 类定义
class Person { //默认public权限 // 大括号内是类体构成 }
2, 定义一个空类
class Empty
3, 在类中定义成员函数:
class Runoob() { fun foo() { print("Foo") } // 成员函数 }
4, 属性定义
class Person{
var name: String = "wwf" //默认public
private var age: Int= 12//私有的属性
}
5, 创建类实例:
val person= Person() // Kotlin 中没有 new 关键字
//属性调用
person.name
6, Koltin 中的类可以有一个 主构造器,以及一个或多个次构造器,主构造器是类头部的一部分,位于类名称之后:
如果主构造器没有任何注解,也没有任何可见度修饰符,那么constructor关键字可以省略。
class Person constructor(firstName: String) {//主构造器 } //等价于 class Person(firstName: String) {} 没有主函数, 也可以如:自定义线性布局 ,继承使用 : 接口和类都是 : 类后面要添加(), 接口不用
class MyLinearLayout : LinearLayout { constructor(context: Context) : super(context) {//次构造器 } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {//次构造器 } constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {//次构造器 } } //等价于 有主构造函数, 可以不写次构造函数,@JvmOverloads 关键字告诉虚拟器生成java代码, 会生成java的三个构造函数class MyLinearLayout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : LinearLayout(context, attrs, defStyleAttr){}
7, getter 和 setter
属性声明的完整语法:
var <propertyName>[: <PropertyType>] [= <property_initializer>] [<getter>] [<setter>]var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setter 可读可写val inferredType = 1 // 类型为 Int 类型,默认实现 getter 只读属性实例:
class Person { var lastName: String = "zhang" get() = field.toUpperCase() // 将变量赋值后转换为大写 set var no: Int = 100 get() = field // 后端变量 set(value) { if (value < 10) { // 如果传入的值小于 10 返回该值 field = value } else { field = -1 // 如果传入的值大于等于 10 返回 -1 } } var heiht: Float = 145.4f private set } // 测试 fun main(args: Array<String>) { var person: Person = Person() person.lastName = "wang" println("lastName:${person.lastName}") person.no = 9 println("no:${person.no}") person.no = 20 println("no:${person.no}") }
8, 非空属性必须在定义的时候初始化,kotlin提供了一种可以延迟初始化的方案,使用 lateinit 关键字描述属性:
class MyLinearLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
private lateinit var mContext: Context//成员属性 不可为空
private lateinit var mText: String//成员属性 不可为空
//也可以这样声明 private var mText : String? = null 可空属性
fun setText(text: String) {
this.mText = text
}
init {//构造函数初始化
this.mContext = context
}
fun test(){
//声明时, 带? 使用时, 也要带? 和Java if(mText == null){} 等价
//使用lateinit(延迟初始化, 使用前, 必须初始化, 否则报异常)声明的变量使用时, 不用加? 但是会报空指针异常
mText?.plus("你好")
}
}
9, 抽象类, 抽象是面向对象编程的特征之一,类本身,或类中的部分成员,都可以声明为abstract的。
抽象成员在类中不存在具体的实现。
注意:抽象类和接口, 默认是open修饰的, 普通类默认final类, 不能被继承。 所有类, 不加public默认都是public权限
open class Base {
open fun f() {
}
}
abstract class Derived : Base() {
override abstract fun f()
}
10,嵌套类
class Outer { // 外部类 private val bar: Int = 1 class Nested { // 嵌套类 fun foo() = 2 } } fun main(args: Array<String>) { val demo = Outer.Nested().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性 println(demo) // == 2 }
11, 内部类
内部类使用 inner 关键字来表示。
内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。
class Outer {
private val bar: Int = 1
var v = "成员属性"
/**嵌套内部类**/
inner class Inner {
fun foo() = bar // 访问外部类成员
fun innerTest() {
var o = this@Outer //获取外部类的成员变量
println("内部类可以引用外部类的成员,例如:" + o.v)
}
}
}
fun main(args: Array<String>) {
val demo = Outer().Inner().foo()
println(demo) // 1
val demo2 = Outer().Inner().innerTest()
println(demo2) // 内部类可以引用外部类的成员,例如:成员属性
}
12, 匿名内部类
class Test { var v = "成员属性" fun setInterFace(test: TestInterFace) { test.test() } } /** * 定义接口 */ interface TestInterFace { fun test() } fun main(args: Array<String>) { var test = Test() /** * 采用对象表达式来创建接口对象,即匿名内部类的实例。 */ test.setInterFace(object : TestInterFace { override fun test() { println("对象表达式创建匿名内部类的实例") } }) }click点击事件
mTextView?.setOnClickListener {//大括号内使用 it 代指调用者 //里面处理点击事件 }
13, 修饰符
类的修饰符包括 classModifier 和_accessModifier_:
// classModifier: 类属性修饰符,标示类本身特性。
kotlin java abstract // 抽象类 abstract final // 类不可继承,默认属性 final enum // 枚举类 enum open // 类可继承,类默认是final的 没被final修饰的类 annotation // 注解类 annotationaccessModifier: 访问权限修饰符
kotlin java private // 仅在同一个文件中可见 private protected // 同一个文件中或子类可见 protected public // 所有调用的地方都可见 public internal // 同一个模块中可见 没有修饰符,
十四, 继承 Any所有类的超类, 类似于java中的Object
Kotlin 中所有类都继承该 Any 类,它是所有类的超类,对于没有超类型声明的类是默认超类:
class Example // 从 Any 隐式继承
Any 默认提供了三个函数:
equals() hashCode() toString()
如果一个类要被继承,可以使用 open 关键字进行修饰。
open class Base(p: Int) // 定义基类 class Derived(p: Int) : Base(p)
如果子类有主构造函数, 则基类必须在主构造函数中立即初始化。
open class Person(var name : String, var age : Int){// 基类 } class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) { } // 测试 fun main(args: Array<String>) { val s = Student("Runoob", 18, "S12346", 89) println("学生名: ${s.name}") println("年龄: ${s.age}") println("学生号: ${s.no}") println("成绩: ${s.score}") }
子类没有主构造函数
如果子类没有主构造函数,则必须在每一个二级构造函数中用 super 关键字初始化基类,或者在代理另一个构造函数。初始化基类时,可以调用基类的不同构造方法。
class Student : Person { constructor(ctx: Context) : super(ctx) { } constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) { } }
重写: 在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它(抽象函数除外, 默认是open的), 子类重写方法使用 override 关键词:
/**用户基类**/ open class Person{ open fun study(){ // 允许子类重写 println("我毕业了") } } /**子类继承 Person 类**/ class Student : Person() { override fun study(){ // 重写方法 println("我在读大学") } } fun main(args: Array<String>) { val s = Student() s.study(); }
属性重写 :
属性重写使用 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 }
十五, 接口
Kotlin 接口与 Java 8 类似,使用 interface 关键字定义接口,允许方法有默认实现
interface Demo{ fun add(a:Int, b:Int):Int // 未实现 fun minus(a: Int, b: Int): Int { //已实现 return a - b } }
一个类或者对象可以实现一个或多个接口
class SubDemo: Demo{
override fun add(a:Int, b:Int) : Int {
// 方法体
}
}
接口中的属性只能是抽象的,不允许初始化值,接口不会保存属性值,实现接口时,必须重写属性
interface Demo { var name:String } class SubDemo : Demo{ override var name:String = "wwf"//重写属性 }
函数重写: 实现多个接口时,可能会遇到同一方法继承多个实现的问题。例如:
interface Demo1{ fun foo() { print("A") } // 已实现 fun bar() // 未实现,没有方法体,是抽象的 } interface Demo2 { fun foo() { print("B") } // 已实现 fun bar() { print("bar") } // 已实现 } class Demo3 : Demo1 { override fun bar() { print("bar") } // 重写 } class Demo4 : Demo1, Demo2 { override fun foo() { super<Demo1>.foo() super<Demo2>.foo() } override fun bar() { super<Demo2>.bar() } } fun main(args: Array<String>) { val d = Demo4() d.foo() d.bar() }