Kotlin 教程 在线

2301kotlin 委托

第一位那位老哥有些范型没写,我给他补上了(版本是 1.3)。

import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

class ResourceID() {
    val image_id: String = "101"
    val text_id: String = "102"
}
class ResourceLoader(id: ResourceID) {
    val d: ResourceID = id
    operator fun provideDelegate( thisRef: MyUI, prop: KProperty<*>): ReadOnlyProperty<MyUI, String> { 
        if(checkProperty(thisRef, prop.name)){
            return DellImpl(d)
        }else{
            throw Exception("Error ${prop.name}")
        }
    }
    private fun checkProperty(thisRef: MyUI, name: String):Boolean {
        return name.equals("image") || name.equals("text")
    }
}
class DellImpl(d: ResourceID) : ReadOnlyProperty<MyUI, String> {
    val id: ResourceID = d
    override fun getValue(thisRef: MyUI, property: KProperty<*>): String {
        if(property.name.equals("image"))
            return property.name+"  "+id.image_id
        else
            return property.name+"  "+id.text_id
    }
}
fun  bindResource(id: ResourceID): ResourceLoader {
    var res = ResourceLoader(id);
    return res
}
class MyUI {
    val image by bindResource(ResourceID())
    val text by bindResource(ResourceID())    
    //val webview by bindResource(ResourceID())
}
fun main(args: Array<String>) {
    try{
        var ui = MyUI()
        println(ui.image)
        println(ui.text)
    }catch(e: Exception) {
        println(e.message)
    }
}

// 输出
/**
* image  101
* text  102
*/

2300kotlin 委托

博主大神写的最后一个例子可能有点复杂,对我这样的小白来说可能不大好理解,我自己写一个简单点的例子,大家感受下:

class MyReadOnlyPropertyImpl : ReadOnlyProperty<MyTestClass, String> {
    override fun getValue(thisRef: MyTestClass, property: KProperty<*>): String {
        val s  = "aaa"
        return s;
    }
}
class MyProvider {
    operator fun provideDelegate(thisRef: MyTestClass, prop: KProperty<*>): ReadOnlyProperty<MyTestClass, String> { 
        println("do something") // 这行代码是在getValue方法之外调用的
        val myReadOnlyPropertyImpl = MyReadOnlyPropertyImpl() // 这里才是调用getValue方法的地方
        return myReadOnlyPropetyImpl
    }
}
class MyTestClass {
    val myProvider = MyProvider()
    val myField1: String by myProvider
}
// 测试
fun main(args: Array<String>) {    
    val myTestClass = MyTestClass()
    println(myTestClass.myField1)
}

最后输出:

do something
aaa

如果觉得 MyReadOnlyPropertyImpl 这个类有点多余,也可以省去,直接在 MyProvider 类中返回匿名类对象:

class MyProvider {
    operator fun provideDelegate(thisRef: MyTestClass, prop: KProperty<*>): ReadOnlyProperty<MyTestClass, String> {
        println("do something") // 这行代码是在getValue方法之外调用的
        return object: ReadOnlyProperty<MyTestClass, String> {            // 这里才是调用getValue方法的地方
            override fun getValue(thisRef: MyTestClass, property: KProperty<*>): String {
                return "aaa" 
            }   
        } 
    }
}

怎么样,用匿名类是不是更直观能看出 println("do something") 是在 getValue 方法之外运行,假如 println("do something")这行代码就代表了博主所说的“检查属性一致性”的代码,那么这样做就实现了博主所说的“扩展所委托对象的逻辑,在创建属性时(而不仅在其 getter 或 setter 中)检查属性一致性”;

还有一点大家注意一下,provideDelegate 这个方法的名称是不能随便起的,只能用这个名字,如果换了其它名字就会在 operator 关键字处报 'operator' modifier is inapplicable on this function: illegal function name;最后说明一下这段代码中用到的范型,ReadOnlyProperty<R, T> 要求传入两个范型,第一个范型 R 代表我们要 get 的属性所在类,第二个范型T代表我们要 get 的属性的类型

2299kotlin 委托

提供委托,在哪儿都没有完整的示例,试着自己补充完整,费了不少时间!分享出来后别人看了会说如此简单!或者说不对!不管怎么说,这份辛苦不能白费了,还是分享给大家。看了几家的文档,菜鸟的做得比较好!比较明白的人做的,受益匪浅,谢谢!

class ResourceID() {
    val image_id: String = "101"
    val text_id: String = "102"
}

class ResourceLoader(id: ResourceID) {
    val d: ResourceID = id
    operator fun provideDelegate( thisRef: MyUI, prop: KProperty ): ReadOnlyProperty {
        if(checkProperty(thisRef, prop.name)){
            return DellImpl(d)
        }else{
            throw Exception("Error ${prop.name}")
        }
    }

    private fun checkProperty(thisRef: MyUI, name: String):Boolean {
        if(name.equals("image") || name.equals("text")){
            return true
        }else{
            return false
        }
    }
}

class DellImpl(d: ResourceID) : ReadOnlyProperty {
    val id: ResourceID = d
    override fun getValue(thisRef: MyUI, property: KProperty): String {
        if(property.name.equals("image"))
            return property.name+"  "+id.image_id
        else
            return property.name+"  "+id.text_id
    }
}

fun  bindResource(id: ResourceID): ResourceLoader {
    var res = ResourceLoader(id);
    return res
}

class MyUI {
    val image by bindResource(ResourceID())
    val text by bindResource(ResourceID())
    //val webview by bindResource(ResourceID())
}

fun main(args: Array) {
    try{
        var ui = MyUI()
        println(ui.image)
        println(ui.text)
    }catch(e: Exception) {
        println(e.message)
    }
}

2298Kotlin 泛型

关于星号投射,其实就是*代指了所有类型,相当于Any?

给文中补个例子方便理解:

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)
    }
}

2297Kotlin 数据类与密封类

我的理解密封类就是一种专门用来配合 when 语句使用的类,举个例子,假如在 Android 中我们有一个 view,我们现在想通过 when 语句设置针对 view 进行两种操作:显示和隐藏,那么就可以这样做:

sealed class UiOp {
    object Show: UiOp()
    object Hide: UiOp()
} 
fun execute(view: View, op: UiOp) = when (op) {
    UiOp.Show -> view.visibility = View.VISIBLE
    UiOp.Hide -> view.visibility = View.GONE
}

以上功能其实完全可以用枚举实现,但是如果我们现在想加两个操作:水平平移和纵向平移,并且还要携带一些数据,比如平移了多少距离,平移过程的动画类型等数据,用枚举显然就不太好办了,这时密封类的优势就可以发挥了,例如:

sealed class UiOp {
    object Show: UiOp()
    object Hide: UiOp()
    class TranslateX(val px: Float): UiOp()
    class TranslateY(val px: Float): UiOp()
}

 

fun execute(view: View, op: UiOp) = when (op) {
    UiOp.Show -> view.visibility = View.VISIBLE
    UiOp.Hide -> view.visibility = View.GONE
    is UiOp.TranslateX -> view.translationX = op.px // 这个 when 语句分支不仅告诉 view 要水平移动,还告诉 view 需要移动多少距离,这是枚举等 Java 传统思想不容易实现的
    is UiOp.TranslateY -> view.translationY = op.px
}

以上代码中,TranslateX 是一个类,它可以携带多于一个的信息,比如除了告诉 view 需要水平平移之外,还可以告诉 view 平移多少像素,甚至还可以告诉 view 平移的动画类型等信息,我想这大概就是密封类出现的意义吧。

除此之外,如果 when 语句的分支不需要携带除“显示或隐藏view之外的其它信息”时(即只需要表明 when 语句分支,不需要携带额外数据时),用 object 关键字创建单例就可以了,并且此时 when 子句不需要使用 is 关键字。只有需要携带额外信息时才定义密封类的子类,而且使用了密封类就不需要使用 else 子句,每当我们多增加一个密封类的子类或单例,编译器就会在 when 语句中给出提示,可以在编译阶段就及时发现错误,这也是以往 switch-case 语句和枚举不具备的功能。

最后,我们甚至可以把这一组操作封装成一个函数,以便日后调用,如下:

// 先封装一个UI操作列表
class Ui(val uiOps: List = emptyList()) {
    operator fun plus(uiOp: UiOp) = Ui(uiOps + uiOp)
}

// 定义一组操作
val ui = Ui() +
        UiOp.Show +
        UiOp.TranslateX(20f) +
        UiOp.TranslateY(40f) +
        UiOp.Hide
// 定义调用的函数
fun run(view: View, ui: Ui) {
    ui.uiOps.forEach { execute(view, it) }
}

run(view, ui) // 最终调用