Kotlin学习——属性和字段
Posted DayFight_DayUp
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin学习——属性和字段相关的知识,希望对你有一定的参考价值。
首先我们要分清属性和字段的区别
属性:Java中的属性,通常可以理解为get和set方法。
看下面Java代码:
public class Person
private String name;
private int age;
public String getName()
return name;
public void setName(String name)
this.name = name;
public int getAge()
return age;
public void setAge(int age)
this.age = age;
字段只要有set或get方法,就是属性
字段:,通常叫做“类成员”。使用来承载数据的,
看下面Java代码:
class A
private String s = "xiaohua"; //没有响应的get和set,那么就是字段或者成员变量
Kotlin中,在我的学习之下,好像只有属性和幕后字段,并没有字段(Kotlin中不能有字段)
属性声明
Kotlin的类可以有属性。属性可以⽤关键字var 声明为可变的,否则使⽤只读关键字val
class Address
var name: String = "hua话"
var street: String = "beikejie"
var city: String = "shenzhen"
var state: String? = null
var zip: String = "hello.zip"
要使⽤⼀个属性,只要⽤名称引⽤它即可,就像 Java 中的字段
fun copyAddress(address: Address): Address
val result = Address() // Kotlin 中没有“new”关键字
result.name = address.name // 将调⽤访问器getter
result.street = address.street
return result
getter和setter
下面看一段Kotlin代码:
class Box <T>
var array :ArrayList<T> = ArrayList()
var size : Int
get() = array.size
set(value)
var tmpArray : ArrayList<T> = array.clone() as ArrayList<T>
array= ArrayList(array.size + value)
for ( i in tmpArray)
array.add(i)
val isEmpt :Boolean
get() = this.size ==0
fun add(a : T)
array.add(a)
override fun toString():String
return array.toString()
fun getCapacity():Int
return array.count()
fun remove(a:T)
array.remove(a)
现在做必要的说明:
1. 声明⼀个属性的完整语法是
var propertyName[: PropertyType] [= property_initializer]
[getter]
[setter]
方括号里的是可选的,setter和getter是访问器
2. 属性的初始器(initializer)、getter 和 setter 都是可选的。属性类型如果可以从初始器(或者从其 getter 返回值,如下⽂所⽰)中推断出来,也可以省略。
3. 省略了访问器,默认会自带访问器
class A
var name:String = ""
var age:Int = 0
set(value)
field =value
fun main(args: Array<String>)
var a = A()
a.name ="lala" //调用默认的setter
println(a.name)//调用默认的getter
println(a.age) //就算省略了也会默认带有getter的
4. ⼀个只读属性的语法和⼀个可变的属性的语法有两⽅⾯的不同:1、只读属性的⽤ val 开始代替 var 2、只读属性不允许 setter(上面的Box类中的各个属性有定义getter和setter的示例)
5. ⾃ Kotlin 1.1 起,如果可以从 getter 推断出属性类型,则可以省略它(如Box中isEmpty一样)
val isEmpt :Boolean
get() = this.size ==0
另外:
如果你需要改变⼀个访问器的可⻅性或者对其注解,但是不需要改变默认的实现,你可以定义访问器⽽不定义其实现
var setterVisibility: String = "abc"
private set // 此 setter 是私有的并且有默认实现
var setterWithAnnotation: Any? = null
@Inject set // ⽤ Inject 注解此 setter
幕后字段
Kotlin 中类不能有字段。然⽽,当使⽤⾃定义访问器时,有时有⼀个幕后字段(backing field)有时是必要的。为此 Kotlin 提供⼀个⾃动幕后字段,它可通过使⽤ field 标识符访问
如果像一下代码:
class A
var age:Int = 0
get() = this.age //这样会陷入死循环的
fun main(args: Array<String>)
var a = A()
println(a.age)
上面的代码看似正确,但是实际上是一个死循环
正确的做法如下:
class A
var age:Int = 0
get() = field //使用幕后字段就不会陷入死循环
fun main(args: Array<String>)
var a = A()
println(a.age)
注意:field 标识符只能⽤在属性的访问器内,其他地方不能出现
如果属性⾄少⼀个访问器使⽤默认实现,或者⾃定义访问器通过 field 引⽤幕后字段,将会为该属性⽣成⼀个幕后字段。
但是像如下代码就不会有幕后字段:
val isEmpty :Boolean
get() = this.size ==0 //因为isEmpty只有一个getter访问器,没有默认,且没有使用field关键字,所以没有幕后字段
幕后属性
如果你的需求不符合这套“隐式的幕后字段”⽅案,那么总可以使⽤ 幕后属性(backing property)
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get()
if (_table == null)
_table = HashMap() // 类型参数已推断出
return _table ?: throw AssertionError("Set to null by another thread")
其中_table就是table的幕后属性
从各⽅⾯看,这上面的代码与 Java 相同的⽅式。因为通过默认 getter 和 setter 访问私有属性会被优化,所以不会引⼊函数调⽤开销。
编译期常量
已知值的属性可以使⽤ const 修饰符标记为 编译期常量。这些属性需要满⾜以下要求:
1. 位于顶层(直接定义在包中kt文件中)或者是 object 的⼀个成员
2. ⽤ String 或原⽣类型 值初始化
3. 没有⾃定义 getter
4. 这些属性可以⽤在注解中
//编译时常量,位于顶层就是直接定义在kt里,和class 一个级别
const val NAME = "King"
@Deprecated(NAME) fun f()
class Three()
var name = NAME //访问常量
object Obj
const val NAME ="tiger" //定义在对象里,使用对象表达式
fun main(args: Array<String>)
Obj.NAME
延迟初始化属性
之前我们在第四节就已经说到了延迟初始化属性,这里我们要详细的说明:
看官方语言:
⼀般地,属性声明为⾮空类型必须在构造函数中初始化。然⽽,这经常不⽅便。例如:属性可以通过依赖注⼊来初始化,或者在单元测试的 setup ⽅法中初始化。这种情况下,你不能在构造函数内提供⼀个⾮空初始器。但你仍然想在类体中引⽤该属性时避免空检查。
为处理这种情况,你可以⽤ lateinit 修饰符标记该属性
分析以上的话:
1. 为了避免编译的时候空检查,又想在本类中引用
2. 现在不要初始化,而是要依赖其他组件初始化这个属性。比如使用SpringIOC,或者JUnit测试时才传入参数
为了满足以上两点的需求,kotlin加入了来lateinit延迟初始化
public class MyTest
lateinit var subject: TestSubject
@SetUp fun setup()
subject = TestSubject() //装配的时候才让属性初始化
@Test fun test()
subject.method() // 测试的时候才用,所以才之前装配
该修饰符只能⽤于在类体中(不是在主构造函数中)声明的 var 属性,并且仅当该属性没有⾃定义 getter 或 setter 时。该属性必须是⾮空类型,并且不能是原⽣类型
上面得到的信息:
1. lateinit只能修饰var类型的属性(因为要调用setter)
2. 原生类型(Byte,Short,Int,Long,Float,Double,Char,Boolean)不能使用lateinit修饰,也就是说,一旦属性是原生类型就要必须被初始化
在初始化前访问⼀个 lateinit 属性会抛出⼀个特定异常,该异常明确标识该属性被访问及它没有初始化的事实
其实这个异常就是:kotlin.UninitializedPropertyAccessException异常 ,是在调用getter之前没有被初始化就会抛出的
以上是关于Kotlin学习——属性和字段的主要内容,如果未能解决你的问题,请参考以下文章
kotlin 中的 Observable.combineLatest 类型推断
Kotlin类与对象 ① ( 成员属性 | Kotlin 自动为成员字段生成 getter 和 setter 方法 | 手动设置成员的 getter 和 setter 方法 | 计算属性 )