20、嵌套类和内部类概述
类定义在一个类的内部叫做嵌套类
内部类分静态内部类(static修饰)和非静态内部类(无static修饰)
嵌套类(相当于静态内部类):放在一个类的内部,用static修饰
内部类(非静态内部类):使用inner修饰,相当于java中的无static修饰非静态内部类
只需要把一个类放在另一个类的类内部嵌套定义即可,甚至可以在方法中定义嵌套类
21、内部类
//通过主构造器为外部类定义属性
class Cow(var weight:Double=0.0){
//定义一个内部类(用inner修饰,相当于java的非静态内部类)
//通过主构造器为内部定义属性
private inner class Cowleg(var length:Double=0.0,var cololr:String=""){
//内部类的方法
fun info(){
println("当前牛腿颜色是:${cololr},高:${length}")
//直接访问外部类的private修饰的foo()方法
foo()
}
}
fun test(){
val c1=Cowleg(1.12,"黑白相间")
c1.info()
}
private fun foo() {
println("Cow的foo方法")
}
}
fun main(args:Array<String>){
val cow=Cow(378.9)
cow.test()
}
打印:
当前牛腿颜色是:黑白相间,高:1.12
Cow的foo方法
如果外部类属性、内部类属性与内部类中方法的局部变量同名,则可通过使用this、带标签的this@(访问外部类属性)来进行限定区分
关于this,kotlin的处理规则如下
- 在类的方法或属性中,this代表该方法或属性的对象
- 在类的构造器中,this代表该构造器即将返回的队形
- 在扩展行数或带接收者的函数字面值中,this表示(.)左边的接收者
- 如果this没有限定符,那么优先代表包含该this的最内层的接收者,并且会自动向外搜索,如果要让this明确引用特定的接收者,则可使用标签限定符
class DiscernVariable{//隐式标签@DiscernVariable
private val prop="外部类的属性"
inner class InClass{//隐式标签@InClass
private val prop="内部类的属性"
fun info(){
val prop="局部变量"
//通过外部类类名.this.varname来访问外部类属性
println("外部类的属性值:${this@DiscernVariable.prop}")
//通过this.varname来访问内部类属性
println("内部类的属性值:${this.prop}")
//直接访问局部变量
println("局部变量的值${prop}")
}
}
fun test(){
val ic=InClass()
ic.info()
}
}
fun main(args:Array<String>){
DiscernVariable().test()
}
打印:
外部类的属性值:外部类的属性
内部类的属性值:内部类的属性
局部变量的值局部变量
====================
内部类成员不能够外部类直接使用,如果需要访问外部类成员,则必须显式创建内部类对象来调用访问其成员
class Outer{
private val outProp=9
inner class Inner{
val inprop=5
fun accessOuterProp(){
//内部类可以直接访问外部类的private属性
println("外部类的outProp值:${outProp}")
}
}
fun accessInnerProp(){
//如果需要访问内部类的属性,必须显式地创建内部类的对象
println("内部类的inProp值:${Inner().inprop}")
}
}
fun main(args:Array<String>){
val out=Outer()
out.accessInnerProp()
}
打印
内部类的inProp值:5
22、嵌套类
由于kotlin取消static修饰符,所以kotlin类的成员除嵌套类之外,全部都是非静态成员,因此嵌套类不可访问外部类的其他任何成员(只能访问其他嵌套类)
class NestedClassTest{
var prop=5
fun test(){
println("外部类的test()方法")
}
//没有inner修饰符,是嵌套类(相当于java的静态内部类)
class NestedClass {
fun accessOuterMember(){
//访问另一个嵌套类是允许的
val a = A()
//下面两行会出错
//println(prop)不可以访问,外部类的成员
//test()
}
}
class A{}
}
fun main(args:Array<String>){
val out=Outer()
out.accessInnerProp()
}
嵌套类唯一可以访问的是外部类的其他嵌套类
嵌套类相当于外部类的静态成员,因此外部类的所有方法,属性,初始化快都可以使用嵌套类来定义变量,创建对象等
外部类依然不能直接访问嵌套类的成员,但可以使用嵌套类的对象作为调用者来访问嵌套类的成员
class NestedClassTest{
class NestedClass {
var prop=9
}
fun accessNetedProp(){
println(NestedClass().prop)
}
}
23、在外部类以外使用内部类
外部类在内部使用嵌套类或者内部类是与平常使用普通类没有太大的区别,一样可以直接通过嵌套类或内部类的类名来定义变量,调用嵌套类或者内部类的构造器来创建实例
嵌套类智能访问外部的其他嵌套类,不能访问外部的其他任何成员。如果希望在外部意外的地方使用内部类或嵌套类,一定要注意访问权限的限制。
在外部类以外的地方定义内部类变量的语法格式
var |val varname:OuterClass.InnerClass
从语法格式上看,在外部类意外的地方使用内部类时,内部类完整的类名应该是OuterClass.InnerClass,如果外部类还有包名,应当加上包名前缀
由于内部类的对象必须寄生在外部类的对象中,因此在创建内部类对象之前,必须先创建器外部类对象。在外部类意外的地方创建内部类实例的语法格式如下
OuterInstance.InnerConstructor()
从以上语法格式可以看出,在外部类以外的地方创建内部类实例时必须使用外部类实例来调用内部类的构造器
class Outer{
//定义一个内部类,不使用访问控制符,默认是public
inner class In(msg:String){
init {
println(msg)
}
}
}
fun main(args:Array<String>){
var oi:Outer.In=Outer().In("测试信息")
//以上代码等效于
//使用OuterClass.InnerClass形式定义内部类变量
//var oi:Outer.In
//创建外部类实例
//val ot=Outer()
//通过外部类实例来调用内部类的构造器创建内部实例
//oi=ot.In("测试信息")
}
测试信息
24、在外部类以外使用嵌套类
由于内部类是外部类本身的成员,因此创建嵌套内部类对象无需创建外部类对象
在外部类以外的地方创建嵌套类实例的语法格式如下
OuterClass.NestedConstructor()
案例
class NestedOut{
//定义一个嵌套类,不适用访问控制符,默认是public
open class Nested{
init {
println("嵌套类的构造器")
}
}
}
fun main(args:Array<String>){
val nested:NestedOut.Nested=NestedOut.Nested()
}
打印
嵌套类的构造器
25、局部嵌套类
- 放在方法或函数中定义的嵌套类就叫做局部嵌套类。
- 局部嵌套类尽在该方法或函数中有效,由于局部嵌套了诶不能在方法或函数以外的地方使用,因此局部嵌套类也不能使用访问控制符修饰
- 如果需要用局部嵌套类定义变量,创建实例或派生子类,只能在局部嵌套类所在的方法或函数中进行。
class LocalNestedClass{
fun info(){
open class NestedBase(var a:Int=0){
}
//定义局部嵌套类的子类
class NestedSub(var b:Int=0):NestedBase(){
}
//创建局部嵌套类的对象
val ns=NestedSub()
ns.a=5
ns.b=8
println("NestedSub对象的a和b属性是:${ns.a},${ns.b}")
}
}
fun main(args:Array<String>){
LocalNestedClass().info()
}
打印
NestedSub对象的a和b属性是:5,8
26、匿名内部类
kotlin抛弃了java的匿名内部类功能,提供了更加强大的语法,对象表达式
27、对象表达式
对象表达式和匿名内部类区别
匿名内部类只能知道你个一个父类型(父接口或父类)
对象表达式课指定0-n个父类型(父接口或父类)
对象表达式的语法格式如下
object[:0-个父类型]{
//对象表达式的类体部分
}
对象表达式还有如下规则
- 对象表达式不能是抽象类,应为要创建实例
- 对象表达式不能定义构造器,但对象表洒水可以定义初始化快,可以通过初始化快来完成构造器需要完成的事情
- 对象表达式可以包含内部类,但不能包含嵌套类
interface Outputable {
fun output(msg:String)
}
abstract class Product(var price:Double){
abstract val name:String
abstract fun printInfo()
}
fun main(args:Array<String>){
//指定一个父类型或接口的对象表达式
var ob1=object :Outputable{
override fun output(msg: String) {
for(i in 1..6){
println("<h${i}>${msg}</h${i}>")
}
}
}
ob1.output("疯狂教育中心")
println("------")
//指定零个父类型的对象表达水
var ob2=object{
//初始化快
init {
println("初始化快")
}
//属性
var name="kotlin"
//方法
fun test(){
println("test方法")
}
//只能包含内部类,不能包含嵌套类
inner class Foo
}
println(ob2.name)
ob2.test()
println("------")
//指定两个父类型的对象表达式
//由于Product只有一个带参数的构造器,因此需要传入构造器参数
var ob3=object:Outputable,Product(28.8){
override fun output(msg: String) {
println("输出信息:+msg")
}
override val name:String
get() = "激光打印机"
override fun printInfo() {
println("高速激光大衣你急,支持自动双面打印")
}
}
println(ob3.name)
ob3.output("kotlin真不错")
ob3.printInfo()
}
打印:
<h1>疯狂教育中心</h1>
<h2>疯狂教育中心</h2>
<h3>疯狂教育中心</h3>
<h4>疯狂教育中心</h4>
<h5>疯狂教育中心</h5>
<h6>疯狂教育中心</h6>
------
初始化快
kotlin
test方法
------
激光打印机
输出信息:+msg
高速激光大衣你急,支持自动双面打印
28、对象声明和单例模式
语法格式为:
object ObjectName[:0-个父类型]{
//对象表达式的类体部分
}
对象声明和对象表达式的区别
- 对象表达式是一个表达式,不能包含内部类,而对象声明不是表达式,不能赋值
- 对象声明课把傲寒嵌套类,不能包含内部类,而对象表达式可包含内部类,不能包含嵌套类
- 对象声明不能定义在函数和方法内,但对象表达式可嵌套在其他对象声明或非内部类中
interface Outputable {
fun output(msg:String)
}
abstract class Product(var price:Double){
abstract val name:String
abstract fun printInfo()
}
//指定一个父类型(接口)的对象表达式
object Myobject1:Outputable{
override fun output(msg: String) {
for(i in 1..6){
println("<h${i}>${msg}</h${i}>")
}
}
}
//指定零个父类型(接口)的对象表达式
object Myobject2{
//初始化快
init {
println("初始化快")
}
//属性
var name="kotlin"
//方法
fun test(){
println("test方法")
}
//只能包含内部类,不能包含嵌套类
class Foo
}
//指定两个父类型的对象表达式
//由于Product只有一个带参数的构造器,因此需要传入构造器参数
object Myobject3:Outputable,Product(28.8){
override fun output(msg: String) {
println("输出信息:+msg")
}
override val name:String
get() = "激光打印机"
override fun printInfo() {
println("高速激光打印机,支持自动双面打印")
}
}
fun main(args:Array<String>){
Myobject1.output("疯狂教育中心")
println("------")
println(Myobject2.name)
Myobject2.test()
println("------")
println(Myobject3.name)
Myobject3.output("kotlin真不错")
Myobject3.printInfo()
}
打印
<h1>疯狂教育中心</h1>
<h2>疯狂教育中心</h2>
<h3>疯狂教育中心</h3>
<h4>疯狂教育中心</h4>
<h5>疯狂教育中心</h5>
<h6>疯狂教育中心</h6>
------
初始化快
kotlin
test方法
------
激光打印机
输出信息:+msg
高速激光打印机,支持自动双面打印
29、伴生对象和静态成员
- 在类中定义的对象声明,可使用companion修饰,这样该对象就变成了伴生对象
- 每个类最多只能定义一个伴生对象,伴生对象相当于外部类的对象,程序可以通过外部类直接调用伴生对象的成员。
interface Outputable {
fun output(msg:String)
}
class Myclass{
//使用compantion修饰的伴生对象
companion object Myobject1:Outputable{
val name="name属性值"
override fun output(msg: String) {
for (i in 1..6){
println("<h${i}>${msg}</h${i}>")
}
}
}
}
fun main(args:Array<String>){
//使用伴生对象所在的类调用伴生对象的方法
Myclass.output("疯狂教育")//使用类名直接调用
println(Myclass.name)//使用类名直接调用
}
打印:
<h1>疯狂教育</h1>
<h2>疯狂教育</h2>
<h3>疯狂教育</h3>
<h4>疯狂教育</h4>
<h5>疯狂教育</h5>
<h6>疯狂教育</h6>
name属性值
从以上代码中可以看到,伴生对象的名称不重要(myobject1根本没用到),因此伴生对象可以省略名称。省略名称后,如果程序真的要访问伴生对象,则可以他通过companion名称进行访问
interface Outputable {
fun output(msg:String)
}
class Myclass{
//使用compantion修饰的伴生对象
companion object :Outputable{
val name="name属性值"
override fun output(msg: String) {
for (i in 1..6){
println("<h${i}>${msg}</h${i}>")
}
}
}
}
fun main(args:Array<String>){
//使用伴生对象所在的类调用伴生对象的方法
Myclass.output("疯狂教育")//使用类名直接调用
println(Myclass.Companion)//使用类名调用companion关键字
}
30、伴生对象的扩展
为伴生对象扩展的方法和属性,就相当于为伴生对象所在的外部类扩展了静态成员,可通过外部类的类名访问这些扩展成员。
interface Outputable {
fun output(msg:String)
}
class Myclass{
//使用compantion修饰的伴生对象
companion object :Outputable{
val name="name属性值"
override fun output(msg: String) {
for (i in 1..6){
println("<h${i}>${msg}</h${i}>")
}
}
}
}
//为伴生对象扩展的方法
fun Myclass.Companion.test(){
println("为伴生对象扩展的方法")
}
//为伴生对象扩展的属性
val Myclass.Companion.foo
get() = "为伴生对象扩展的属性"
fun main(args:Array<String>){
//使用伴生对象所在的类调用伴生对象的方法
Myclass.output("疯狂教育")//使用类名直接调用
println(Myclass.name)//使用类名调用companion关键字
//通过伴生对象所在对的类调用为伴生对象扩展的成员
Myclass.test()
println(Myclass.foo)
}
打印
<h1>疯狂教育</h1>
<h2>疯狂教育</h2>
<h3>疯狂教育</h3>
<h4>疯狂教育</h4>
<h5>疯狂教育</h5>
<h6>疯狂教育</h6>
name属性值
为伴生对象扩展的方法
为伴生对象扩展的属性
31、枚举类入门
枚举类是一种特殊的类,可以有自己的属性、方法,可以实现一个或多个接口,也可以定义自己的构造器
枚举类与普通类的区别
- 枚举类可以实现一个或多个接口,继承自kotlin.Enum类
- 使用enum定义的非抽象的枚举类不能使用open修饰,不能派生子类
- 枚举类的构造器只能使用private访问控制符,如果省略了构造器的访问控制符,则默认private修饰
- 枚举类的所有实例必须在枚举类的第一行显式列出
枚举类默认提供如下两个方法
- EnumClass.valueOf(value:String):EnumClass
- EnumClass.values:Array<EnumClass>
案例
enum class Season{
//在第一行列出4个枚举实例
SPRING,SUMMER,FALL,WINTER
}
fun main(args:Array<String>){
//枚举默认有一个values()方法,返回该枚举类所有实例(因为不是继承自Object,而是继承制ENUM类)
for (s in Season.values()){
println(s)
}
val seasonName="SUMMER"
val s:Season=Season.valueOf(seasonName)
println("-----")
println(s)
println("-----")
//直接访问枚举值
println(Season.WINTER)
}
打印
SPRING
SUMMER
FALL
WINTER
-----
SUMMER
-----
WINTER
由于枚举类继承自kotli.ENUM类,父类提供了如下属性和方法
- name属性,返回此枚举实例的名称,也就是枚举值之一
- ordinal属性,返回枚举值在枚举类中的索引值,从零0
- intcompareTo(E o)方法:用于与指定对的枚举对象比较顺序
- String toString():返回枚举常量的名称,与name属性相似,但toString方法用得更多
32、枚举类的属性、方法、构造器
- 枚举类式设计为不可变类,因此他的属性值不允许改变,这样会更安全,因此尽职开发者对属性赋值,并推荐是用val为枚举声明只读属性
- 由于是只读属性,枚举必须在构造器中为这些属性指定初始值(或在初始化快中指定初始值,但少用)因此应当为枚举类显示定义带参数的构造器
- 一旦为枚举类显式地定义了带参数的构造器你,在列出枚举值时就必须对应地传入参数
//使用主构造器声明cnname只读属性
enum class Gender(var cnName:String){
MALE("男"),FAMALE("女");//因为后面有额外的枚举成员,这里用了分号结束,等同有MALE=new Gender("男")
//定义方法
fun info(){
when(this){
MALE-> println("天行健,君子以自强不息")
FAMALE-> println("唯小人与女子难养也")
}
}
}
fun main(args:Array<String>){
//通过Gender的valueOf()方法更久枚举名获取枚举值
val g=Gender.valueOf("FAMALE")
//访问枚举值的cnName属性
println("${g}代表${g.name}")
//调用info方法
g.info()//g不是枚举值吗,怎么又成了枚举实例了,因为枚举类的每一枚举值就是一个枚举实例
}
打印:
FAMALE代表FAMALE
唯小人与女子难养也
33、实现接口的枚举类
枚举类实现接口也需要实现该接口所包含的方法
interface GenderDesc {
fun info()
}
//使用主构造器声明cnname只读属性
enum class Gender(var cnName:String):GenderDesc{
MALE("男"),FAMALE("女");//因为后面有额外的枚举成员,这里用了分号结束,等同有MALE=new Gender("男")
//定义方法
override fun info(){
when(this){
MALE-> println("天行健,君子以自强不息")
FAMALE-> println("唯小人与女子难养也")
}
}
}
fun main(args:Array<String>){
//通过Gender的valueOf()方法更久枚举名获取枚举值
val g=Gender.valueOf("FAMALE")
//访问枚举值的cnName属性
println("${g}代表${g.name}")
//调用info方法
g.info()//g不是枚举值吗,怎么又成了枚举实例了,因为枚举类的每一枚举值就是一个枚举实例
}
打印:
FAMALE代表FAMALE
唯小人与女子难养也
34、包含抽象方法的抽象枚举类
- 枚举类中包含抽象方法时依然不能使用abstract修饰,系统会自行添加上去
- 在定义每个枚举值时必须为抽象成员提供实现
案例:每个枚举操作定义具体的计算方法,来实现抽象计算方法
enum class Operation{
PLUS{
override fun eval(x: Double, y: Double): Double {
return x+y
}
},
MINUS{
override fun eval(x: Double,y: Double)=x-y
},
TIMS{
override fun eval(x: Double, y: Double): Double {
return x*y
}
},
DIVIDE{
override fun eval(x: Double, y: Double): Double {
return x/y
}
};
//为枚举类定义一个抽象方法,这个抽象方法由不同的枚举值提提供不同的实现
abstract fun eval(x:Double,y: Double):Double
}
fun main(args:Array<String>){
println(Operation.PLUS.eval(2.0,4.0))
println(Operation.MINUS.eval(5.0,3.0))
println(Operation.TIMS.eval(3.0,6.0))
println(Operation.DIVIDE.eval(5.0,4.0))
}
打印:
6.0
2.0
18.0
1.25
35、类委托
kotlin的委托分为类委拖和属性委托
类委托的本质就是将本类需要实现的部分方法委托给了其他对象----相当于借用其他对象的方法作为自己的实现。
使用by关键字进行委托
interface Outputable {
fun output(msg:String)
var type:String
}
//定义一个DefaultOuput类实现outputable接口
class DefaultOutput:Outputable{
override fun output(msg: String) {
for (i in 1..6){
println("<h${i}>${msg}</h${i}>")
}
}
override var type:String="输出设备"
}
//定义Printer类,指定构造参数b作为委托对象
class Printer(b:DefaultOutput):Outputable by b
//定义projecto类,指定新建的对象作为委托对象
class Projector():Outputable by DefaultOutput(){
override fun output(msg: String) {
javax.swing.JOptionPane.showMessageDialog(null,msg)
}
}
fun main(args:Array<String>){
val output=DefaultOutput()
//printer对象的委托对象是output
var printer=Printer(output)
//其实就是调用委托对象的output()方法
printer.output("疯狂教育机构")
//其实就是调用委托对象的type属性方法
println(printer.type)
//projector对象的委托对象也是output
var projector =Projector()
//projector本身重写了outpu方法,所以此处是调用本类重写的方法
projector.output("疯狂软件中心")
//其实就是调用委托对象的type属性方法
println(projector.type)
}
打印:
<h1>疯狂教育机构</h1>
<h2>疯狂教育机构</h2>
<h3>疯狂教育机构</h3>
<h4>疯狂教育机构</h4>
<h5>疯狂教育机构</h5>
<h6>疯狂教育机构</h6>
输出设备
输出设备
36、属性委托(没阿太看懂)
属性委托可以将多个类的类似属性同意交给委托对象集中实现,避免每个类都要单独实现这些属性
对于制定了委托对象的属性而言,由于她的实现逻辑已经交给了委托对象处理,因此开发者不能为委托属性提供getter和setter方法。
一旦将某个对象指定为属性的未委托对象,该对象就会全面接管该属性读取getter和写入setter操作,因此,属性的未谈妥对象无需实现任何接口。但一定要提供一个getValue()方法和setValue()方法
使用operator修饰符
import kotlin.reflect.KProperty
class PropertyDelegation{
//该属性的委托对象是MyDelegation
var name:String by MyDelegation()
}
class MyDelegation{
private var _backValue="默认值"
operator fun getValue(thisRef:PropertyDelegation,property:KProperty<*>):String{
println("${thisRef}的${property.name}属性执行getter方法")
return _backValue
}
operator fun setValue(thisRef: PropertyDelegation,property: KProperty<*>,newValue:String){
println("${thisRef}的${property.name}属性执行settter方法,传入参数值为:${newValue}")
_backValue=newValue
}
}
fun main(args:Array<String>){
val pd=PropertyDelegation()
//读取属性,实际上是调用属性的委托对象的getter方法
println(pd.name)
//写入属性,实际上是调用属性的委托对象的setter方法
pd.name="疯狂教育中心"
println(pd.name)
}
打印:
PropertyDelegation@3c0ecd4b的name属性执行getter方法
默认值
PropertyDelegation@3c0ecd4b的name属性执行settter方法,传入参数值为:疯狂教育中心
PropertyDelegation@3c0ecd4b的name属性执行getter方法
疯狂教育中心
37、延迟属性
- kotlin提供了一个lazy()函数,该函数接受一个lambda表达式作为参数,并返回一个lazy< T >对象
- lazy < T > 对象包含了一个符合只读属性委托要求的getValue()方法,因此该lazy对象可作为只读属性的委托对象
val lazyProp:String by lazy {
println("第一次访问时执行代码块")
"疯狂软件中心"
}
fun main(args:Array<String>){
//两次访问lazyProp属性的值
println(lazyProp)//第一次正常加载
println(lazyProp)//第二次加载不在计算lambda表达式,而是直接返回第一次的值
}
打印:
第一次访问时执行代码块
疯狂软件中心
疯狂软件中心
lazy < T >的getValue()方法的处理逻辑是,第一次调用该方法适合,程序会计算lambda表达式,并得到其返回值,以后程序再次调用该方法时,不再计算lambda表达式,而是直接使用第一次计算得到的返回值
38、属性监听
两个监听方法
- fun < T >oberservable
- fun < T >vetoable
import kotlin.properties.Delegates
var observableProperty:String by Delegates.observable("默认值"){
//lambda表达式的第一个参数代表被监听的属性
//第二个参数代表修改之前的值
//第三个参数代表被设置的新值
property, oldValue, newValue ->
println("${property}的${oldValue}被改为${newValue}")
}
fun main(args:Array<String>){
//访问observableProp属性,不会触发监听器的lambda表达式
println(observableProperty)
//设置属性值,触发监听器的lambda表达式
observableProperty="疯狂软件"
println(observableProperty)
}
打印:
默认值
var observableProperty: kotlin.String的默认值被改为疯狂软件
疯狂软件
import kotlin.properties.Delegates
var vetoableProp:String by Delegates.vetoable("默认值"){
//lambda表达式的第一个参数代表被监听的属性
//第二个参数代表修改之前的值
//第三个参数代表被设置的新值
property, oldValue, newValue ->
println("${property}的${oldValue}被改为${newValue}")
newValue>oldValue
}
fun main(args:Array<String>){
//访问vetoable属性,不会触发监听器的lambda表达式
println(vetoableProp)
//新值小于旧值,监听的lambda表达式false,新值设置失败
vetoableProp= 15.toString()
println(vetoableProp)
//新值大于旧值,监听的lambda表达式true,新值设置成功
vetoableProp= 25.toString()
println(vetoableProp)
}
打印:
默认值
var vetoableProp: kotlin.String的默认值被改为15
默认值
var vetoableProp: kotlin.String的默认值被改为25
默认值
39、使用Map存储属性值
kotlin的map提供了如下方法:
operator fun....Map...getValue
MutableMap提供两个方法
operator fun....Map...getValue
operator fun....Map...setValue
- 意味着mutableMap对象可作为读写对象的委托
- 程序将类制度属性委托给Map对象管理,在这种情况下,对象本身并不负责存储对象状态,而是将对象状态保存在Map集合中。
- 这么做的好处是,当程序需要和外部接口通信时,程序并不需要将该对象直接暴露出来,只要将该对象属性所委托的Map暴露出来即可,而Map则完整地保存了整个对象状态。
- 下例将所有的只读属性都委托给一个Map对象,Item对象的每个属性名就相当于Map的key,属性值就相当于key对应的value
class Item(val map:Map<String,Any?>){
val barCode:String by map
val name:String by map
val price:Double by map
}
fun main(args:Array<String>){
val item=Item(mapOf(
"barCode" to "12354",
"name" to "疯狂kotlin讲义",
"price" to 68.9
))
println(item.barCode)
println(item.name)
println(item.price)
println("*********")
//将对象持有的map暴露出来,其他程序可以通过标准map读取数据
val map=item.map
println(map["barCode"])
println(map["name"])
println(map["price"])
}
打印
12354
疯狂kotlin讲义
68.9
12354
疯狂kotlin讲义
68.9
上例改为MutableMap也是成立的,并且另外加了写的属性
class MutableItem(val map:MutableMap<String,Any?>){
val barCode:String by map
val name:String by map
val price:Double by map
}
fun main(args:Array<String>){
val item=MutableItem(mutableMapOf(
"barCode" to "12354",
"name" to "疯狂kotlin讲义",
"price" to 68.9
))
println(item.barCode)
println(item.name)
println(item.price)
println("*********")
//将对象持有的map暴露出来,其他程序可以通过标准map读取数据
val map=item.map
println(map["barCode"])
println(map["name"])
println(map["price"])
map["price"]=999.9
println(item.price)
}
打印
12354
疯狂kotlin讲义
68.9
12354
疯狂kotlin讲义
68.9
999.9
40、局部属性委托
指定了委托对象的局部变量诚挚为“局部委托属性”-其实还是局部变量。只是对该变量的读取、赋值操作都交给了委托对象去实现
局部变量的委托对象,对于只读属性要实现operator修饰的getValue方法,该方法第一个参数是Nothing?类型。代表永不存在的对象
同样的以上读写属性也类似
import kotlin.reflect.KProperty
class Mydelegation{
private var _backValue="默认值"
operator fun getValue(thisRef:Nothing?,property:KProperty<*>):String{
println("${thisRef}的${property.name}属性执行getter方法")
return _backValue
}
operator fun setValue(thisRef:Nothing?,property:KProperty<*>,newValue:String) {
println("${thisRef}的${property.name}属性执行setter方法,传入参数值为${newValue}")
_backValue=newValue
}
}
fun main(args:Array<String>){
var name:String by Mydelegation()
//访问局部变量,实际是调用mydelegete对象的getValue()方法
println(name)
//对局部变量赋值,实际是调用mydelegete对象的setValue()方法
name="新的值"
println(name)
}
打印
null的name属性执行getter方法
默认值
null的name属性执行setter方法,传入参数值为新的值
null的name属性执行getter方法
新的值
可以利用lazy()函数对局部变量延迟初始化
41、委托工厂(真心没懂)
委托工厂对象也可以作为委托对象,委托工厂需要提供如下方法
operator fun provideDelegate:该方法的参数与委托的getValue方法的两个参数的意义相同
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class MyTarget{
//该属性的委托对象是provideDelegate()方法返回的mydelegation对象
var name:String by PropertyChecker()
}
class PropertyChecker () {
operator fun providerDelegate(thisRef: MyTarget,property: KProperty<*>):ReadWriteProperty<MyTarget,String>{
//插入自定义代码,可执行任意业务操作
checkProperty(thisRef,property.name)
//返回实际委托对象
return Mydelegation()
}
private fun checkProperty(thisRef: MyTarget, name: String) {
println("检查属性")
}
}
class Mydelegation:ReadWriteProperty<MyTarget,String>{
private var _backValue="默认值"
override fun getValue(thisRef:Nothing?,property:KProperty<*>):String{
println("${thisRef}的${property.name}属性执行getter方法")
return _backValue
}
override fun setValue(thisRef:Nothing?,property:KProperty<*>,newValue:String) {
println("${thisRef}的${property.name}属性执行setter方法,传入参数值为${newValue}")
_backValue=newValue
}
}
fun main(args:Array<String>){
//创建对象(初始化属性),调用委托工厂的providerDelegate()方法
val pd=MyTarget()
//读取属性,实际上是调用属性委托对象的getter方法
println(pd.name)
//写入属性,实际上是调用属性委托对象的setter方法
pd.name="疯狂教育"
println(pd.name)
}