美文网首页
Kotlin-基础笔记整理二

Kotlin-基础笔记整理二

作者: roczheng | 来源:发表于2018-12-27 09:34 被阅读0次
    1、接口:

    interface 关键字定义接口,允许方法有默认实现

    • 接口中的属性
      只能是抽象的,不允许初始化值,接口不会保存属性值,实现接口时,必须重写属性
    interface MyInterface {
        var name:String //name 属性, 抽象的
        fun bar()    // 未实现
        fun foo() {  //已实现
            // 可选的方法体
            println("foo")
        }
    }
    class Child : MyInterface {
        override var name: String = "runoob" //重写属性
        override fun bar() {
            // 方法体
            println("bar")
        }
    }
    fun main(args: Array<String>) {
        val c = Child()
        c.foo()
        c.bar()
        println(c.name)
    }
    
    • 输出
      foo
      bar
      runoob
    2、扩展函数:

    在已有类中添加新的方法,不会对原类做修改,扩展函数定义形式

    fun receiverType.functionName(params){
        body
    }
    

    receiverType:表示函数的接收者,也就是函数扩展的对象
    functionName:扩展函数的名称
    params:扩展函数的参数,可以为NULL

    • 示例1:
    class User(var name:String)
    /**扩展函数**/
    fun User.Print(){
        print("用户名 $name")
    }
    fun main(arg:Array<String>){
        var user = User("Runoob")
        user.Print()
    }
    
    • 输出
      用户名 Runoob

    • 示例2:
    //MutableList可变列表
    // 扩展函数 swap,调换不同位置的值
    fun MutableList<Int>.swap(index1: Int, index2: Int) {
        val tmp = this[index1]     //  this 对应该列表
        this[index1] = this[index2]
        this[index2] = tmp
    }
    fun main(args: Array<String>) {
        val l = mutableListOf(1, 2, 3)//向可变列表存入值
        // 位置 0 和 2 的值做了互换
        l.swap(0, 2) // 'swap()' 函数内的 'this' 将指向 'l' 的值
        println(l.toString())
    }
    
    • 输出
      [3, 2, 1]

    扩展函数是静态解析的,并不是接收者类型的虚拟成员,在调用扩展函数时,具体被调用的的是哪一个函数,由调用函数的的对象表达式来决定的,而不是动态的类型决定的

    open class C
    
    class D: C()
    
    fun C.foo() = "c"   // 扩展函数 foo
    
    fun D.foo() = "d"   // 扩展函数 foo
    
    fun printFoo(c: C) {
        println(c.foo())  // 类型是 C 类
    }
    fun main(arg:Array<String>){
        printFoo(D())
    }
    
    • 输出
      c

    • 若扩展函数和成员函数一致,则使用该函数时,会优先使用成员函数。

    • 扩展一个空对象
    fun Any?.toString(): String {
        if (this == null) return "This is null"
        // 空检测之后,“this”会自动转换为非空类型
        // 解析为 Any 类的成员函数
        return toString()
    }
    fun main(arg:Array<String>){
        var t1 = 123
        println(t1.toString())
        var t2 = null
        println(t2.toString())
    }
    
    • 输出
    123
    This is null
    

    • 扩展属性
    val <T> List<T>.lastIndex: Int
        get() = size - 1
    

    展属性允许定义在类或者kotlin文件中,不允许定义在函数中。初始化属性因为属性没有后端字段(backing field),所以不允许被初始化,只能由显式提供的 getter/setter 定义。

    val Foo.bar = 1 // 错误:扩展属性不能有初始化器
    

    扩展属性只能被声明为 val

    3、伴生对象:
    • Kotlin 中,在类中定义的对象(object)声明,可使用 companion 修饰,此对象(object)就是伴生对象
    • 示例:
    class NumberTest {
        companion object Obj {  
            var flag = false
            fun plus(num1: Int, num2: Int): Int {
                return num1 + num2
            }
        }
    }
    

    一个类(class)中最多只能定义一个伴生对象。

    • 调用
      使用 companion 关键字修改的对象之后,伴生对象就相当于是外部类的对象,可以使用类直接调用。
    • 示例:
    fun main(args: Array<String>) {
        println(NumberTest.plus(1, 2))  // 3
        println(NumberTest.flag)  // false
    }
    
    • 伴生对象的作用

    类似于 Java 中使用类访问静态成员的语法。因为 Kotlin 取消了 static 关键字,所以 Kotlin 引入伴生对象来弥补没有静态成员的不足。可见,伴生对象的主要作用就是为其所在的外部类模拟静态成员。

    • 如果声明伴生对象有名称,则使用:
      类名.伴生对象名.方法名()
      类名.半生对象名.属性的setter,getter方法

    例:NumberTest.Obj.plus(2, 3)

    • 如果声明伴生对象无名称,则采用 Companion 关键字调用:
      类名.Companion.方法名()
      类名.Companion.属性的setter,getter方法

    例:NumberTest.Companion.getFlag()

    更多:伴生对象

    • 扩展的作用域
    package foo.bar
    fun Baz.goo() { …… } 
    

    使用所定义包之外的一个扩展, 通过import导入扩展的函数名进行使用:

    package com.example.usage
    
    import foo.bar.goo // 导入所有名为 goo 的扩展
                       // 或者
    import foo.bar.*   // 从 foo.bar 导入一切
    
    fun usage(baz: Baz) {
        baz.goo()
    }
    
    • 扩展声明为成员
    class D {
        fun bar() {
            println("D bar")
        }
    
        fun beer() {
            println("我是D类的beer方法")
        }
    }
    
    class C {
        fun baz() {
            println("C baz")
        }
    
        fun beer() {
            println("我是C类的beer方法")
        }
    
        fun D.foo() {//声明为D的扩展函数
            bar()//调用D.bar
            baz()//调用C.baz
            //-----------------------------------------
            beer()       //调用D.bar(),扩展接受者优先
            this@C.beer()//调用C.bar()
        }
    
        fun caller(d: D) {
            d.foo()//调用扩展函数
        }
    }
    
    fun main(args: Array<String>) {
        val c: C = C()
        val d: D = D()
        c.caller(d)
    }
    

    输出:
    D bar
    C baz
    我是D类的beer方法
    我是C类的beer方法

    4、数据类:
    • 数据类
      创建一个只包含数据的类,关键字为 data
    data class User(val name: String, val age: Int)
    

    数据类需要满足以下条件:

    主构造函数至少包含一个参数。
    所有的主构造函数的参数必须标识为val 或者 var ;
    数据类不可以声明为 abstract, open, sealed 或者 inner;
    数据类不能继承其他类 (但是可以实现接口)。

    • 复制
      复制使用 copy() 函数,我们可以使用该函数复制对象并修改部分属性,
    fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
    
    • 示例
    data class User(val name: String, val age: Int)
    
    fun main(args: Array<String>) {
        val jack = User(name = "Jack", age = 1)
        val olderJack = jack.copy(age = 2)
        println(jack)
        println(olderJack)
    }
    

    输出
    User(name=Jack, age=1)
    User(name=Jack, age=2)

    5、泛型:
    • 示例
    class Box<T>(t : T) {//声明一个泛型类
        var value = t
    }
    fun main(args: Array<String>) {
        var boxInt = Box<Int>(10)//定义类型为整形
        var boxString = Box<String>("Runoob")//定义类型为字符串
    
        println(boxInt.value)
        println(boxString.value)
    }
    
    • 输出
      10
      Runoob

    在调用泛型函数时,如果可以推断出类型参数,可以省略泛型参数。

    • 泛型约束
      常见的约束是上界(upper bound):
    fun <T : Comparable<T>> sort(list: List<T>) {
        // ……
    }
    
    • 示例
    sort(listOf(1, 2, 3)) // OK。Int 是 Comparable<Int> 的子类型
    sort(listOf(HashMap<Int, String>())) // 错误:HashMap<Int, String> 不是 Comparable<HashMap<Int, String>> 的子类型
    

    Kotlin 中没有通配符类型,它有两个其他的东西:声明处型变(declaration-site variance)与类型投影(type projections)。

    • 类型协变

    在类型声明时,使用协变注解修饰符(in或者out)。于这个注解出现在类型参数的声明处, 因此我们称之为声明处的类型变异。

    internal interface Source<in T, out R> {
        fun mapT(t: T): Unit
        fun nextR(): R
    }
    

    in T: 来确保Source的成员函数只能消费T类型,而不能返回T类型
    out R:来确保Source的成员函数只能返回R类型,而不能消费R类型

    • 星号投射
    class A<T>(val t: T, val t2 : T, val t3 : T)
    class Apple(var name : String)
    fun main(args: Array<String>) {
        //使用类    
        val a1: A<*> = A(12, "String", Apple("苹果"))
        val a2: A<Any?> = A(12, "String", Apple("苹果"))   //和a1是一样的
        val apple = a1.t3    //参数类型为Any
        println(apple)
        val apple2 = apple as Apple   //强转成Apple类
        println(apple2.name)
        //使用数组
        val l:ArrayList<*> = arrayListOf("String",1,1.2f,Apple("苹果"))
        for (item in l){
            println(item)
        }
    }
    
    6、Kotlin 枚举类:

    枚举常量用逗号分隔,每个枚举常量都是一个对象

    enum class Color{
        RED,BLACK,BLUE,GREEN,WHITE
    }
    
    • 枚举初始化
    enum class Color(val rgb: Int) {
        RED(0xFF0000),
        GREEN(0x00FF00),
        BLUE(0x0000FF)
    }
    
    • 使用枚举常量
    enum class Color {
        RED, BLACK, BLUE, GREEN, WHITE
    }
    
    fun main(args: Array<String>) {
        var color: Color = Color.BLUE
        println(Color.values())//以数组的形式返回枚举值
        println(Color.valueOf("RED"))//输出:RED
        println(color.name)//输出:BLUE
        println(color.ordinal)//输出:2
    }
    

    自 Kotlin 1.1 起,可以使用 enumValues<T>() 和 enumValueOf<T>() 函数以泛型的方式访问枚举类中的常量 :

    enum class RGB { RED, GREEN, BLUE }
    inline fun <reified T : Enum<T>> printAllValues() {
        print(enumValues<T>().joinToString { it.name })
    }
    fun main(args: Array<String>) {
        printAllValues<RGB>() // 输出 RED, GREEN, BLUE
    }
    
    7、对象表达式:
    • 示例1
    open class A1(x: Int) {
         open val y: Int = x
    }
    
    interface B {
    }
    
    fun main(args: Array<String>) {
        val ab: A1 = object : A1(1), B {
            override val y = 15
        }
        println("当前y的值" + ab.y)
    }
    
    • 输出
      当前y的值15

    • 示例2
    open class A1(x: Int) {
        open val y: Int = x
    }
    
    interface B {
        fun printB() {
            println("我是B的方法")
        }
    }
    
    fun main(args: Array<String>) {
        val ab: B = object : A1(1), B {    //使用B接口来接收
            override val y = 15
        }
        println("调用B的方法" + ab.printB())
    }
    
    • 示例3
    fun main(args: Array<String>) {
        val site = object {
            var name: String = "大吉大利,今晚吃鸡"
            var url: String = "www.baidu.com"
        }
        println(site.name)
        println(site.url)
    }
    

    匿名对象可以用作只在本地和私有作用域中声明的类型。如果你使用匿名对象作为公有函数的 返回类型或者用作公有属性的类型,那么该函数或属性的实际类型 会是匿名对象声明的超类型,如果你没有声明任何超类型,就会是 Any。在匿名对象 中添加的成员将无法访问。

    class CC {
        // 私有函数,所以其返回类型是匿名对象类型
        private fun foo() = object {
            val x: String = "x"
        }
    
        // 公有函数,所以其返回类型是 Any
        fun publicFoo() = object {
            val x: String = "x"
        }
    
        fun bar() {
            val x1 = foo().x        // 没问题
            val x2 = publicFoo().x  // 错误:未能解析的引用“x”,访问不到
        }
    }
    
    8、对象声明:
    object Site {
        var url:String = ""
    }
    fun main(args: Array<String>) {
        var s1 =  Site
        var s2 = Site
        s1.url = "www.baidu.com"
        println(s1.url)
        println(s2.url)
    }
    

    与对象表达式不同,当对象声明在另一个类的内部时,这个对象并不能通过外部类的实例访问到该对象,而只能通过类名来访问,同样该对象也不能直接访问到外部类的方法和变量。

    class Site {
        var name = "大吉大利"
        object DeskTop{
            var url = "www.baidu.com"
            fun showName(){
                print{"desk legs $name"} // 错误,不能访问到外部类的方法和变量
            }
        }
    }
    fun main(args: Array<String>) {
        var site = Site()
        site.DeskTop.url // 错误,不能通过外部类的实例访问到该对象
        Site.DeskTop.url // 正确
    }
    
    9、对象表达式和对象声明之间的语义差异:
    • 对象表达式是在使用他们的地方立即执行的

    • 对象声明是在第一次被访问到时延迟初始化的

    • 伴生对象的初始化是在相应的类被加载(解析)时,与 Java 静态初始化器的语义相匹配

    相关文章

      网友评论

          本文标题:Kotlin-基础笔记整理二

          本文链接:https://www.haomeiwen.com/subject/dawpkqtx.html