Kotlin - 属性(gettersetter、lateinit)

Kotlin - 属性(gettersetter、lateinit),第1张

属性声明

权限修饰符 var|val 属性名称:属性类型 = 初始化语句

Kotlin 中非抽象的属性必须初始化,或者使用 latrinit 延迟初始化。属性不能缺少初始化语句,要么在定义属性的地方、要么在构造中、要么在 init{ } 代码块里,否则会编译错误。声明为 val 的属性不能有自定义的 set() 语句,因为不能再次赋值。成员属性初始化顺序:主构造 > 声明处 > init{ } > 次构造
//id在主构造中初始化
class Demo(var id: Long, grade: Int) {
    //grade在主构造中初始化
    var grade = grade
    //age在声明处初始化
    var age = 18
    //name在 init{} 中初始化
    var name: String
    init {
        this.name = "张三"
    }
    //hobbit在次构造中赋值
    var hobbit: String? = null
    constructor() : this(123L, 5) {
        this.hobbit = "打球"
    }
}
幕后字段 Backing Field

        在Kotlin中属性作为一级语言特性,通常情况下集幕后字段(field储值变量)+ 访问器(getter读访问器、setter写访问器)于一身,无论是声明【var age: Int】、赋值【user.age = 18】、取值【println(user.age)】,从字面上看都是 age 这个属性本身,是一个整体。而只有在我们需要自定义访问器的时候才会区分这三者。

        例如自定义 setter 的时候,如果不写成幕后字段 field = value,不管是 age = value 还是 this.age = value 都会报错,因为属性 age 的赋值就是setter,显然不能递归调用。

        如果属性的访问器至少有一个使用默认实现,或者自定义的访问器中使用了 field ,那么就会提供幕后字段,用 field 关键字表示,主要用于自定义 getter/setter 时使用,也只能在 getter/setter 中访问。

class Demo{
    var id: Long = 0
        get() = field
        set(value) { field = value }
}
访问器 getter & setter
一般用于让属性在不同条件下有不同值。使用 var 修饰的属性默认拥有 getter 和 setter,使用 val 修饰的属性默认只有 getter。可见性默认和字段的可见性一致,也可以自定义。使用 实例.属性名 的写法会自动编译为 getter 或 setter,同时允许自定义 getter 和 setter,只能在类体中定义。getter 没有参数列表,返回值类型与属性类型相同因此可以自动推导不写。不能在 getter 里再调用本属性,会出现无限循环导致堆栈溢出错误。setter 是一个没有返回值的函数。单个参数的时候一般使用 value 表示(非必须),field 表示本属性的值(相当于this.本属性名)。字段初始化后,getter 或 setter 里才能使用 field,属性未初始化,getter 和 setter 必须同时声明且不能使用 field。
class Demo() {
    //默认自动生成
    var id: Long = 0
        get() = field
        set(value) { field = value }
    //后接等号表达式或值
    var age = 18
        get() = 20
        set(value) = if (value > 0) field = value else field = 0
    //后接大括号语句
    var name = "haha"
        get() { return "getter" }
        set(value) { field = "settet" + value }
}
类外的属性

在类外定义的属性是包级的,该属性会被编译为单独的一个类(原类名Kt)。单独想调用该类的话,导包后Kotlin直接使用字段,Java调用getXXX()同名方法。

//aa.Demo.kt
val number = 10086    //类体外定义的包级属性
class Demo(){}

//编译会生成一个DemoKt类,反编译后内容如下
public final class DemoKt{
    private static final int number = 10086;    //number被编译为这个类的私有静态字段
    public static final int getNumber(){        //并拥有一个默认的getXXX()同名方法
        return number;
    }
}

//Koltin使用
import aa.number    //因为是包级变量,使用 包名.属性 名方式导入
val b = number    //直接使用字段

//Java使用
import aa.DemoKt;    //导入自动生成的class
int b = DemoKt.getNumber();    //使用默认的getXXX()同名方法

编译期常量 const val 定义在类外,反编译后可以看到它就是Java中的常量,但是有几点限制:

只能定义在类外或对象(Object)内。只能使用 String 或 基本数据类型 初始化。不能自定义 getter(不需要可直接调用)。
//aa.Demo.kt
const val number = 10086
class Demo(){}

//反编译内容如下:
public final class DemoKt{
    public static final number = 10086;
}

Kotlin中使用在注解参数中的属性,只能是编译期常量(其他形式的属性都不能使用在注解的参数里)。

const val DEPRECATED_MESSAGE = "This is deprecated."
@Deprecated(DEPRECATED_MESSAGE) fun foo() {……}
lateinit 延迟初始化 & by lazy 惰性初始化

Kotlin 中非抽象的属性必须初始化,当有些属性不需要在创建实例的时候初始化或者需要外部来初始化,可以使用 latrinit 来延迟初始化。

编译器会把 lateinit 修饰的变量在这个类中所有方法方法遍历一遍,看有没有对其进行初始化,没有就会报错。不可修饰基本数据类型和可空类型。因为非空的对象类型可用 null 标记未初始化并在访问的时候引发异常,而基本数据类型和可空的类型没办法用null标记。不能有自定义的 getter/setter。lateinit 只能用在 var 类型上,并可在任意位置初始化多次,有支持域(Backing Fields)。by lazy 只能用在 val 类型上,在第一次调用的时候就被初始化,再次改变只能重定义。
class Demo() {
    //lateinit初始化
    lateinit var name: String
    fun initName(){ name = "张三" }    //函数名可以随便取
    fun show() = ::name.isInitialized   //检查是否初始化
    //by lazy初始化
    val id: Long by lazy {
        println("初始化:" + System.currentTimeMillis())
        123L
    }
}

fun main() {
    val aa = Demo()
    println(aa.show())  // 打印:false
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/992747.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存