还有一点大家注意一下,provideDelegate 这个方法的名称是不能随便起的,只能用这个名字,如果换了其它名字就会在 operator 关键字处报 'operator' modifier is inapplicable on this function: illegal function name;最后说明一下这段代码中用到的范型,ReadOnlyProperty<R, T> 要求传入两个范型,第一个范型 R 代表我们要 get 的属性所在类,第二个范型T代表我们要 get 的属性的类型
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 进行两种操作:显示和隐藏,那么就可以这样做:
除此之外,如果 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) // 最终调用
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) }最后输出:
如果觉得 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) // 最终调用