Kotlin学习之路(五):属性,Getters 与 Setters

前言

本章节记录下kotlin的属性,以及比较重要的Getters 与 Setters的使用。在之前的章节中,简单的介绍了属性(成员变量)Kotlin学习之路(四):类,构造函数,对象,接下来将会比较详细的介绍kotlin的属性相关使用。

一.类的属性

1.1 属性初始化

对于kotlin来说属性可以用val修饰,也可以用var修饰。

class Person {
	// val和var修饰的属性必须初始化

	// var修饰的属性
    var name:String = "Jack"
    
    // val修饰的属性
    val id = 1001
}

fun main(){
    val person = Person() // 创建一个只读的Person对象
//  var person = Person()
	person.id = 1 // 报错,val修饰的属性是只读,不能写入
    println(person.name)
    println(person.age)
    println(person.id)
}

通过上面的代码实例,可以知道,var可读可写,val只能读取,不能写入。背后的编译原理可以参考我之前的文章:kotlin var,val,const val修饰符编译
varval还有几点需要注意:

  1. 一般情况下,无论val和var必须初始化。
  2. 访问属性和java一样使用.访问。
  3. 属性前面不加任何访问权限修饰符,代表默认访问权限修饰符:public

1.2 属性延迟初始化

一般情况下必须立刻对属性初始化,当然也可以延迟初始化,这里就需要使用by lazylateinit

1.2.1 lateinit

class Person {
    lateinit var name:String // 这里不需要初始化
}

fun main() {
    val person = Person()
    person.name = "Tom" // 这里初始化,到这里就和java一样了
    println("name is ${person.name}")
}

对于lateinit有以下这么几个特点:

  1. lateinit只能用于var修饰的属性
  2. lateinit只能修饰非基本数据类型,比如,不能修饰Int,Float等基本数据类型

1.2.2 by lazy

对于by lazy也有以下这么几个特点:

  1. 只能修饰val的属性。
  2. 具备延迟性,只有在使用的时候才会初始化一次。

例子:

class Person {
    val name:String by lazy {
        println("初始化!")
        "Tom"
    }
}

fun main() {
    val person = Person()
    println("name is ${person.name}") // 第一次使用的时候才初始化
    println("-------------------------------")
    println("name is ${person.name}")
}

以上两点中最重要的是第二点,被修饰的属性只在被使用的时候才会初始化。上面的例子执行的结果如下:

初始化!
name is Tom
-------------------------------
name is Tom

仔细看看上面的结果,首次使用的时候才会初始化,再使用的时候就不需要初始化,而是直接执行结果!

二.getter方法和setter方法

2.1 Java的getter和setter方法

首先我们先来回顾下Java的getter方法和setter方法:

public class Person {
    private String mName;

    private int mAge;

    public String getName() {
        return mName;
    }

    public void setName(String mName) {
        this.mName = mName;
    }

    public int getAge() {
        return mAge;
    }

    public void setAge(int mAge) {
        this.mAge = mAge;
    }
}

上面是一个最简单的带有getter和setter的类。通过隐藏mNamemAge,向外暴露name和age的getter和setter。

2.2 kotlin的getter和setter方法

上面是Java的getter和setter的简单介绍,kotlin也有setter和getter,同时也更加的简单:

class Person {
    var name:String = ""

    var age:Int = 0
}

对,就这么简单,不信?看下反编译之后的代码:

public final class Person {
   @NotNull
   private String name = "";
   private int age;

   @NotNull
   public final String getName() {
      return this.name;
   }

   public final void setName(@NotNull String var1) {
      Intrinsics.checkNotNullParameter(var1, "<set-?>");
      this.name = var1;
   }

   public final int getAge() {
      return this.age;
   }

   public final void setAge(int var1) {
      this.age = var1;
   }
}

通过反编译可以看到,生成的代码里面已经自动加上了getter和setter方法。如果你做java和kotlin混合开发,其他的java代码就可以直接调用kotlin的getter和setter方法。

三 Kotlin的get()和set()默认方法

3.1 get()和set()默认方法

kotlin有种Java所不具备的可以给每个属性变量设置一个get()和set()默认方法:
基本语法:

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

(来自kotlin官方文档:Kotlin:属性)

其中的getter和setter就是你"可选的"get()和set()默认方法。
接下来看下这些默认方法的简单使用:
get():

class Person {
    var age:Int = 0
        get() = -1
}

fun main() {
    val person = Person()
    println("age is ${person.age}") // age is -1
    person.age = 22
    println("age is ${person.age}") // age is -1
}

set():

class Person {
    var age:Int = -1
        set(value){
            if (value <= 0){
                field = 0
            }
        }
}

fun main() {
    val person = Person()
    println("age is ${person.age}") // age is -1
    person.age = -10
    println("age is ${person.age}") // age is 0
}

看到上面的代码,是不是感到有点迷糊,是的,我刚开始也感到有些迷糊!
接下来我总结下:

  1. 对于set()默认方法只有在属性变量被赋值的时候才会调用该方法。
  2. 对于get()默认方法只有在获取(使用)属性变量的时候才会调用该方法。

针对上面的两个总结,举一个例子:
set()

class Person {
    var age:Int = -1
        set(value){
            println("调用set()")
        }
}

fun main() {
    val person = Person()
    println("age is ${person.age}")
    person.age = -10
    println("age is ${person.age}")
}

输出结果:

age is -1
调用set()
age is -1

注意!这里的"调用set()"打印语句,就是在执行person.age = -10的时候调用的。

get()

class Person {
    var age:Int = -1
        get(){
            println("调用get()")
            return field
        }
}

fun main() {
    val person = Person()
    println("age is ${person.age}")
}

输出结果:

调用get()
age is -1

同理"调用get()"则是在调用person.age的时候执行的。

通过上面的例子,可以了解set()和get()的调用时机,那么get和set默认方法到底是怎么实现的?很简单,反编译成Java看看:
kotlin代码

class Person {
    var age:Int = -1
        set(value){
            if (value <= 0){
                field = 0
            }
        }
}

反编译成Java后:

public final class Person {
   private int age = -1;

   public final int getAge() {
      return this.age;
   }

   public final void setAge(int value) {
      if (value <= 0) {
         this.age = 0;
      }
   }
}

看到上面反编译后的Java代码,聪明的你应该就知道了,get和set默认方法其实就是类的getter和setter方法。

3.2 get()和set()默认方法的权限修饰符

对于get()和set()默认方法是可以加上权限修饰符的,比如private,protected。

class Person {
    var age:Int = -1
        private set
//        protected get // 这里直接报错(Getter visibility must be the same as property visibility)
}

fun main(){
    val  person = Person()
//    person.age = 22 // 报错,age不能被赋值
}
  1. 对于set()默认方法,显示权限修饰符的范围不能超过该属性的显示权限修饰符的范围。
  2. 对于get()默认方法,它的显示权限必须和属性的显示权限一样。

我们可以通过这一个特性,轻松将某个成员变量改为不能赋值的成员变量。

3.3 幕后字段

幕后字段其实就是field,一般情况下在get和set默认方法中可以隐藏。

class Person {
    var age:Int = -1
        get(){
            println("调用get()")
            return field
        }
}

field指代的就是那个属性变量,上面的例子就是指age。这里做一个大胆的假设,如果将field改为age会如何?
field改为age
事实上将会出现java.lang.StackOverflowError栈溢出的问题。所以,以后在get和set默认方法内部写一些逻辑的时候,注意不要使用属性变量,而应该是使用field。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Kotlin中,可以使用get和set函数来定义属性的访问器。在主构造函数中,属性可以通过声明接收下来成为变量后直接使用。例如,在主构造函数中定义属性的例子如下所示: ``` class Test03(_name:String,_sex:Char,_age:Int,_info:String) { var sex=_sex get() = field var age=_age get() = field var info=_info get() = field var name=_name get() = field private set(value) {field=value} fun show(){ println(name) println(sex) println(info) println(age) } } fun main() { val test03 = Test03("李渐渐", '女', 10, "信息") test03.show() } ``` 另一种方式是在主构造函数中直接定义属性,如下所示: ``` class Test04(var name:String,var sex:Char,val age:Int,val info:String) { fun show(){ println(name) println(sex) println(info) println(age) } } fun main() { val test04 = Test04("李渐渐", '男', 10, "信息") test04.show() } ``` 除了主构造函数外,还可以使用次构造函数来初始化属性。在使用次构造函数时,必须先调用主构造函数,然后执行次构造函数中的内容。例如: ``` class Test05 (name:String ) { constructor(name: String,info:String):this(name){ println("$info") } constructor(name: String,sex:Char,info:String):this(name){ println("${info}和$sex") } constructor(name: String,sex:Char,info:String,age:Int):this(name){ println("${info}和${sex}和${age}和${name}") } } fun main() { Test05("名字", '男', "信息",88) } ``` 在构造函数中还可以使用默认参数,简化代码。例如: ``` class Test06 (name: String?="李小龙") { constructor(name: String?="李小龙",info:String?="信息"):this(name){ println("${info}和${name}") } constructor(name: String?="李小龙",sex:Char='男',info:String="信息"):this(name){ println("${info}和$sex") } constructor(name: String?="李小龙",sex:Char='男',info:String="信息",age:Int=82):this(name){ println("${info}和${sex}和${age}和${name}") } } fun main() { Test06() //运行结果: 信息和男和82和李小龙 Test06(null,null) //运行结果: null和null Test06(age=56) //运行结果: 信息和男和56和李小龙 } ``` 在构造函数中可以使用init代码块对属性进行初始化。init代码块是在主构造函数执行之后立即执行的。例如: ``` class Test08 (_age:Int,val sex:Char){ val derry="AAA" val age=_age init { val ageText=age println("主构造函数被调用了: $ageText") } constructor(name:String, age:Int, sex:Char):this(55,'男'){ println("次构造函数被调用了: $name") } val text="CVV" } fun main() { Test08(50,'男') Test08("王",78,'女') } ``` 最后,如果想延迟初始化某个属性,可以使用lateinit关键字。此关键字用于标记变量,在声明时不初始化,但在使用之前必须对其进行初始化。例如: ``` lateinit var 变量名:变量类型 ``` 希望以上信息能够帮助到您。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Kotlin学习笔记(四)——get/set、主次构造函数、初始化](https://blog.csdn.net/mm731296278/article/details/125968471)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rockyou666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值