Kotlin学习之属性
@(Kotlin学习)
Kotlin中的属性主要有以下几点:
- Kotlin中允许包级属性的存在,即属性不一定在类里;
- 所有非抽象属性都强制要求初始化,没有初始化的属性无法通过编译;
- 标准化的getter和setter;
- 大量的高级属性修饰符。
一、声明属性
Kotlin的类可以有属性。属性可以用关键字var声明为可变的,也可以是使用val声明为只读的。语法格式如下:
[修饰符] val|var 属性名[: 属性类型] [= 初始化语句]
[get() = getter 语句]
[set() = setter 语句]
- 属性不能缺少初始化语句,要么写在定义属性的地方,要么写在init语句里,否则就会编译错误;
- 标记为val的属性不能有自定义的setter,因为Kotlin强烈要求属性初始化,标记为val的属性已经在创建对象的时候初始化了,不能再次赋值。
如下例:
class Person(val name: String){
val age:Int
val id:Long=0
val nationality:String
init{
age=0
}
}
- name属性在主构造方法里定义,使用传入的参数进行初始化;
- age属性在init语句中进行初始化;
- id属性在定义时进行初始化;
- nationality属性不是抽象属性,没有在定义时初始化,也没有在init语句中初始化,所以编译错误。
二、Getters与Setters
Kotlin把Java中没有固定标准的getter和setter方法标准化,并规定调用Kotlin类的属性时强制使用setter和getter方法,不会直接操作累的属性。
可以比较一下Kotlin和Java访问属性的写法的差异:
//Kotlin访问属性
val p = Person("Alex")
println(p.name)
p.name="Bob"
//Java访问属性
Person p = new Person("Alex");
System.out.println(p.getName());
p.setName("Bob");
Kotlin中的”对象.属性”会视情况自动编译为调用getter或setter方法。
一个只读属性的语法和一个可变的属性的语法有两方面的不同:
1. 只读属性的用val开始代替var;
2. 只读属性不允许setter。
Kotlin中允许属性添加自定义的getter和setter:
class Person(name: String){
var name=name
set(value){
field=if (value.isEmpty()) ""else value[0].toUpperCase()+value.substring(1)
}
val isValidName
get() = !name.isEmpty()
}
上面的代码要注意以下几点:
1. 需要自定义setter或getter的属性,不能放在类头里定义,必须在类体内定义;
2. getter是一个没有参数、返回类型与属性类型相同的函数。完整的写法应该如下:
get() : 属性类型{
....
}
- 不能在getter里再调用本属性,因为Kotlin代码里所有对属性的访问都会被编译为getter方法,这样写就会出现无限迭代和StackOverFlowError;
- setter的参数列表一般有一个与属性类型相同的参数,没有返回值;
- Kotlin中一般用value表示这个参数,当然也可以用其他关键字;
- field是表示幕后字段的关键字,在使用时相当于this.name,但是只能在setter方法内。
三、类外属性
在Kotlin类外定义的属性有两种,一是直接写在类外并初始化的包级属性,二是使用constval定义的编译期常量。
- 在类外定义的包级属性,会被编译为一个“文件类”的静态变量;
- 编译器常量使用constval定义在类外,它与包级属性有一定的相似之处。
编译器常量就是Java中常用的常量。Kotlin的编译器常量有一下几点限制:
- 只能定义类外或对象内;
- 只能使用String或原生类型初始化;
- 不能自定义getter。
- Kotlin中,使用在注解参数中的属性,只能是编译期常量(其他形式的属性不能使用在注解的参数里)。
四、延迟初始化属性
一般地,属性声明为非空类型必须在构造函数中初始化。Kotlin强制要求类内定义的非抽象属性都要初始化,但是有些属性不需要在新建实例时初始化,或可能需要外部注入来初始化,这些时候我们都无法按照Kotlin的要求初始化属性。
为了避免编译错误,可以使用lateinit关键字修饰属性。
fun main(args: Array<String>) {
val person = Person("HXL")
person.initHello()
print(person.hello)
}
class Person(val name: String){
lateinit var hello:String
fun initHello(){
hello="Hello,my name is $name"
}
}
使用lateinit关键字修饰hello属性,定义了initHello()函数来初始化hello属性。
如果调用了未初始化的lateinit属性,就会抛出UninitalizedPropertyAccessException。
fun main(args: Array<String>) {
val person = Person("HXL")
print(person.hello)
}
//上述代码所抛异常
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property hello has not been initialized
at Person.getHello(app.kt:7)
at AppKt.main(app.kt:3)