美文网首页
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