美文网首页
属性及可见性

属性及可见性

作者: 一江碎月 | 来源:发表于2018-06-15 13:54 被阅读0次

    分类

    类中的属性有两种:

    1. 可以存储值的属性

    2. 不存储属性值,每一次访问都需要通过 getter / setter 方法,因此每一次访问都需要计算。

      interface Parent {
          val name: String
          val age: Int
              get() {
                  println("getter")
                  return 10
              }
      }
      
      class Child:Parent{
          override val name = "name" // 可以存储值。也可以通过 getter / setter 获取修改
              set(value) {
                  println("setter ${field} ${value}")
                  field = value
              }
      }
      

    getter 与 setter

    1. 在方法内部,使用特殊标识符 field 访问支持字段的值。

      class Child {
          var i = 0
          var name = "name" 
              set(value) {
                  println("setter ${field} ${value}")
                  field = value // 为字段赋值
              } 
              get() {
                  println("get ${field}")
                  i++
                  field = "${i}"
                  return "${i}"
              }
      }
      
      fun main(args: Array<String>) {
          val c = Child()
          println(c.name)
          c.name = "main--"
          println(c.name)
      }
      

      其输出结果为:

    输出结果
    1. 可以修改 getter/setter 的可见性。

      class Test {
          var name = ""
              private set(value) { // 将 setter 可见性设置为 private
                  field = value
              }
      }
      

    可见性

    1. 默认的修饰符是 public

    2. kt 新增修饰符 internal:模板可见。一个模板指的是一起编译的 kt 文件。

    3. protected 只能用于修饰类中的成员

    4. 可见性低的类不能用于可见性高的类、方法、属性

    5. 与 java 一样,重写可以提升可见性,但不能降低可见性

    internal class User()
    
    open class Person()
    
    class Student:Person(){
        internal var user:User = User()
    
        internal fun test():User = User()
    }
    

    上述代码中,由于 User 定义为 internal,所以 Student 类的 user 属性、test() 方法都只能定义成比 internal 或比它低的可见性。

    同理,如果将 Person 类的可见性定义 internal ,则 Student 不能继承,除非将 Student 也定义成 internal 或比它低的可见性。

    修饰符 类成员 顶层声明
    public(默认) 所有地方可见 所有地方可见
    internal 模块中可见 模块中可见
    protected 子类可见 - (不能修饰)
    private 类中可见 文件中可见

    属性委托

    1. 使用关键字 by ,将属性的访问器委托给另一个实例

    2. 属性委托只有在属性被访问时才进行初始化,与 lazy 函数类似。

    格式

    属性委托基本语法如下:

    class Demo{
        var p:Type by Delegate()
    }
    

    属性 p 将自己的 getter/setter 逻辑委托给了另一个对象,这里是 Delegate 的一个实例:通过对 by 后的表达式求值来获取这个实例。上述代码等价如下代码:

    class Demo {
        private val delegate = Delegate()
    
        var p: Type
            get() = delegate.getValue()
            set(value) = delegate.setValue(value)
    }
    

    惰性初始化

    lazy 函数会延迟加载,再结构 by 关键字可以实现委托属性。

    在代码中,经常需要使用 惰性初始化:只有当需要该属性的时候才对属性进行初始化

    如下类,初始化时,并没有初始化 s,只有当访问 s 时才进行初始化,并且将初始化的值存储于 _s 中,这样下次访问时可以直接返回,这就是所谓的惰性初始化:

    class Test {
        private var _s: String? = null
        val s: String
            get() {
                if (_s == null)
                    _s = "sss"
                return _s!!
            }
    }
    

    上述代码有点啰嗦,如果有多个惰性属性,这些模板代码需要写多次。而 属性委托可以让代码变得简单,可以封装用于存储值的支持属性以及确保该值只会被初始化一次。

    委托类的构造

    委托类有如下要求:

    1. 定义 getValue 与 setValue(如果是 val 变量,不需要)方法,且被声明为 operator

    2. 方法至少有两个参数,一个用于接收属性的实例;一个用于表示属性本身,其类型为 KProperty。可以通过 KProperty.name 获取属性名。

    3. 结合关键字 by ,kt 编译器自动完成 ‘惰性初始化‘ 中代码的功能

    fun main(args: Array<String>) {
        val d = Demo()
        println(d.a) // 会输出 getValue ,然后再输出 1
        d.a = 3 // 会输出 setValue
        println(d.a) // 会输出 getValue ,然后再输出 3
    }
    
    class TestDelegate(var propValue: Int) {
        operator fun getValue(demo: Demo, name: KProperty<*>): Int {
            println("getValue")
            return propValue
        }
    
        operator fun setValue(demo: Demo, name: KProperty<*>, newValue: Int) {
            println("setValue")
            propValue = newValue
        }
    }
    
    class Demo {
        var a: Int by TestDelegate(1)
    }
    

    从输出中可以发现:访问 a 属性时,所有的请求都转发到 TestDelegate 对应的方法中。这就是所谓的属性委托。

    相关文章

      网友评论

          本文标题:属性及可见性

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