Kotlin学习之属性

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() : 属性类型{
    ....
}
  1. 不能在getter里再调用本属性,因为Kotlin代码里所有对属性的访问都会被编译为getter方法,这样写就会出现无限迭代和StackOverFlowError;
  2. setter的参数列表一般有一个与属性类型相同的参数,没有返回值;
  3. Kotlin中一般用value表示这个参数,当然也可以用其他关键字;
  4. field是表示幕后字段的关键字,在使用时相当于this.name,但是只能在setter方法内。

三、类外属性

在Kotlin类外定义的属性有两种,一是直接写在类外并初始化的包级属性,二是使用constval定义的编译期常量

  1. 在类外定义的包级属性,会被编译为一个“文件类”的静态变量;
  2. 编译器常量使用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)

学海无涯苦作舟

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员丶星霖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值