Kotlin类和对象 (三)--- 属性和字段

1、回顾下属性和字段的区别

java 中的属性(property),通常可以理解为get和set方法;
而字段(field)通常叫做“类成员”或者“类成员变量”,有时也叫“域”,可以理解为“数据成员”,用来承载数据的。

这两个概念是完全不同的。

1.1 字段(field)

类成员(字段),通常在类中定义的类成员变量,例如:

public class A{

   private String s  = "12";
}

我们可以说 A类中有一个成员变量叫做 s 或者 A 类中有一个字段 s。

字段一般用来承载数据,所以为了安全性,一般定义为私有的。

字段和常量描述了类的数据(域),当这些数据的某些部分不允许外界访问时,根据“对象封装”的原则,应尽量避免将一个类型的字段以公有方式提供给外部。除了 final 修饰的常量。

一般将其设置为 private 类型。既然为私有,那外界怎么访问呢? 当然是通过Java的属性方法!

1.2 属性(property)

属性只局限于类中方法的声明,并不与类中其他成员相关,属于JavaBean的范畴。例如:

void setA(String s){
   a = s;
}
String getA(){

  return a
}

当一个类中拥有这样一对方法时,我们可以说,这个类中拥有一个可读写的 a 属性。如果去掉了set 方法,则是可读属性,反之亦然。

其规则是: 去掉 get 或 set 后剩余的字符串,如果第二个字母是小写的,则把第一个字母也变成小写

getAge —-》 age
getCPU —》 CPU

更形象的说就是:属性是对字段的封装,供外部访问。
通常属性将相应的私有字段通过封装成公共属性,以便于外界访问和修改。当然你在封装成属性时,也可以设置该属性为只读,可读写等等权限。

在Kotlin中,其含义和 在Java 中是一样的。

2、Kotlin 属性和字段

2.1 声明属性

通过 var 声明可变属性,val 声明只读属性。

q1: 为什么叫属性而不是字段?
a1: Kotlin 类中声明的变量,都会提供默认的 get、set(val 没有该方法)方法,所以声明的都是属性。
Kotlin中没有字段,只有幕后字段(backing field).

声明的时候基本都采用以下形式:

class Person {
    // 需要进行初始化,否则提示错误
    var name: String? = null
    var address: String = "上海市"
    var age: Int? = null
    val sex: String = "male"
}

2.2 属性的使用

使用属性,只需要使用名称引用即可:

    val person = Person()
    person.name = "李四"  // 默认调用 set 
    println(person.name) // 默认调用  get,输出 “李四”

注: 对属性的访问,并不是像Java直接访问属性的本身,而是默认调用了 get 和 set 方法。

3、Getters 和 Setters

完整的属性声明如下:

[属性修饰符 (var 、 val)][属性名称][: ][属性类型] = [初始化]
   [getter]
   [setter]

对于【初始化】和【getter】和【setter】都是可选的,如果通过【初始化】的值或者【getter】可以推断出类型,则【类型】也可以省略。

只读属性和可变属性的区别:

  • 只读属性使用 val 声明,可变属性使用 var 声明
  • 只读属性不允许 setter

q2: 如何自定义属性的访问方法,是否会和默认的 setter 或者 getter 冲突?
a2: 见 第 4 点。

4、自定义Getters 和 Setters

Getters 和 Setters 的 编写不同一般的方法,在属性声明内部编写。例如:

Getters:

var name: String? = null
        get(){
          // 重写get方法
          return "lisi"
        }

// 调用
 val person = Person()
    person.name = "李四"  // 默认调用 set 
    println(person.name) // 默认调用  get,输出“lisi”

此处的 name 值一直都是 “lisi”, 无论 name 设置成什么值。

Setters:

set(value){
   // 这是错误的写法,会导致循环调用
   name = value 
}

错误原因:当对属性赋值时就会调用 set 方法,当获取属性的值时 就会 调用 get 方法。所以会一直循环调用 set 方法。

正确的写法是:

set(value){
   field = value 
}

说明:

  • value 是 setter 的参数,其类型同于属性的类型,也可以换成别的名字。
  • field 就是之前提到的 幕后字段 ,用于将真正的值赋给属性,而不会导致循环调用。它只能在属性的访问器中使用。这个也是可选项,有的时候必须(比如 setter),有时不必须(比如 getter).

如果该 幕后字段 还不能满足要求,也可以使用 幕后属性。如:

private var _table: HashMap<String,String>? = null // 私有属性
var table: HashMap<String,String>? = null
     get(){

        if(_table == null){
           _table = HashMap<String,String>()
        }
        return _table // 返回私有属性
     }

上面的代码需要特殊说明一下,当属性被定义为private后 其Getter和Setter,都是私有的,外部都不可以访问也就是说 person._table 是不允许的。也就进行了隐藏。又因为默认的Setter和Getter调用私有属性会被进行优化,所以不会引入函数调用开销。

5、延迟初始化属性

对于非空属性,在声明时必须对其进行初始化,如果想进行延迟初始化,可以使用 lateinit 关键字,代码举例:

 lateinit  var name: String

使用 lateinit 关键字修饰的前提条件:

  • 针对非空属性。反例:lateinit var name: String?
  • 在类体中声明而不是在方法中声明的属性
  • 没有自定义 Setter 或者 Getter
  • 不能是原生类型(String 可以,它不是原生类型)

如果在初始前访问lateinit定义的属性会抛出特定异常,指明该属性没有被初始化。

6、 编译期常量(const 修饰符)

在kotlin中已知值的属性可以使用 const 标记 为 编译期常量。

6.1 const 用法

在kotlin中的顶级属性,会以getter(val 和 var)/setter(var才有)的形式暴露给Java,如果你想让其以public static final的字段呈现给调用者,可以在var 或者val前面加上const修饰符。

使用 const 关键字修饰的前提条件:

  • const只能修饰val,不能修饰var
 正确:const val testName = "ZhangSan"
 报错:const var testName = "ZhangSan"
  • 只能用在顶级属性,以及object对象的属性中(伴随对象也是object)
class Person {
    // 错误
    const val name: String = ""

    // companion object
    companion object {
        // 正确
        const val PERSON_TAG: String = "person"
    }
}

//正确
const val name: String = ""

//object 正确
object myObject {
    const val constNameObject: String = "constNameObject"
    val nameObject: String = "nameObject"
}
  • 没有自定义Getter
// 错误,不能自定义get
const val name: String = ""
    get
    get(){
    }
  • 用String或者原生类型值初始化
// 错误
const val name: String? = ""
// 正确
const val name: String = ""
// 错误
const val age: Int? = 0
// 正确
const val age: Int = 0
  • 如果经过const修饰的字符串属性,去拼接另一个属性,那么这个属性也必须是经过 const 修饰的,否则会报错。
const val age: Int = 10
// 普通属性也可拼接 const 属性
val normalName: String = "Jack $age"
// const 属性必须拼接 const 属性
const val appendName: String = "append name $age"

// const 属性不能拼接 非 const 属性,这个会报错
const val appendName: String = "append name $normalName"

6.2 在java中 和 Kotlin中引用比较

新建一个 Kotlin 文件 Person.kt


const val age: Int = 10

val normalName: String = "Jack is $age years old"

const val constName: String = "Jack is $age years old"

//object
object myObject {
    const val constNameObject: String = "constNameObject"
    val nameObject: String = "nameObject"
}

class Person {
    companion object {
        const val constNameCompanionObject: String = "constNameCompanionObject"
        val nameCompanionObject: String = "nameCompanionObject"
    }

}

在kotlin中使用

fun main(args: Array<String>) {
    println(constName)
    println(normalName)

    println(myObject.constNameObject)
    println(myObject.nameObject)

    println(Person.constNameCompanionObject)
    println(Person.nameCompanionObject)
}

在java 中调用

public class TestJava {

    public static void main(String[] args) {
        System.out.println(PersonKt.constName);
        System.out.println(PersonKt.getNormalName());

        System.out.println(myObject.constNameObject);
        System.out.println(myObject.INSTANCE.getNameObject());

        System.out.println(Person.constNameCompanionObject);
        System.out.println(Person.Companion.getNameCompanionObject());

    }

由此可见,针对纯Kotlin开发来说,有没有const修饰 没有什么影响;
针对和 java 混合式开发来说,有以下说明:

上述在 Kotlin中定义的属性,编译成 java 后 都变成了 static final 的,加 const 的是 public static final,没加const 的 是 private static final ,也就是说,const 的作用 就是把此处默认的 private 变成了public 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值