一起学Kotlin(6)

类:类(Class)是面向对象程序设计(OOP,Object-Oriented Programming)实现信息封装的基础。类是一种用户定义的引用数据类型,也称类类型。每个类包含数据说明和一组操作数据或传递消息的函数。类的实例称为对象。[摘自百度百科]
在Java中可以说我们将类用得非常熟练了,那么在Kotlin中类与Java有什么不同呢?今天我们从类的定义、初始化、继承的异同来一谈究竟。
一 类定义
1.1 创建一个类

class Player{
   
}

与Java一样,使用class关键字 后面跟类名即可定义一个类。
1.2 类属性
类属性在Java中通常也叫为成员变量,可以定义成员变量的访问域、get、set方法等等。在Kotlin中也比较类似,Kotlin针对你定义得每一个属性,Kotlin都会产生一个field、一个getter、一个setter,field用来存储属性数据,你不能直接定义field,Kotlin会封装field并保护它里面得数据,只暴露getter和setter给到使用者使用。getter方法决定你如何读取属性值,每个属性都有getter方法。setter方法决定你如何设置属性值,因为属性也分为只读属性和可变属性,只有可变属性才有setter方法。Kotlin会自动提供默认得getter和setter方法,但在需要控制如何读写属性的时候,你可以自定义这2个方法。

class Player{
    var name = "jack"
        get() = field.capitalize()
        set(value){
            field = value.trim()
        }
    var age = 10
        get() = field.absoluteValue
        set(value) {
            field = value.absoluteValue
        }
    val nickName = "pig"
        get() = field.capitalize()
        //只读变量不能set
//        set(value) {
//
//        }
    var birthday = "2000-01-01"
        private set(value) {
            field = value
        }
}
fun playerTest1(){
    val player = Player()
    println("name=${player.name} age=${player.age} nickName=${player.nickName} birthday=${player.birthday}")
    player.name = "jacky"
    player.age = -20
    //这句会报错提示val变量不能设置
//    player.nickName = "dog"
    //这句会报错提示setter为私有
//    player.birthday = "2001-01-01"
    println("name=${player.name} age=${player.age} nickName=${player.nickName} birthday=${player.birthday}")
}
//结果
name=Jack age=10 nickName=Pig birthday=2000-01-01
name=Jacky age=20 nickName=Pig birthday=2000-01-01

在getter、setter属性的时候,与Java有点不同,我们操作的是一个field的封装属性而不是直接操作属性名来操作。我们可以将getter、setter设置为私有以阻止使用者调用来处理相关属性的获取和更改。
1.3 计算属性
计算属性是通过一个覆盖的get或set运算符来定义,者时候field就不需要了

class Player1{
    val rolledValue
        get() = (1..6).shuffled().first()
}
fun player1Test(){
    val player1 = Player1()
    println(player1.rolledValue)
    println(player1.rolledValue)
}
//结果
1
4

可以看到属性的值是根据计算的结果来返回的,也就是说每次调用getter得到的结果都有可能不一样。

二 初始化
2.1 主构造函数
我们在类的定义中定义一个主构造函数,使用临时变量为类的各个属性提供初始值,在Kotlin中,为便于识别,临时变量(包括仅引用一次的参数),通常都会以下划线开头的名字命名。

class Player3(_name:String,_age:Int){
    var name = _name
    var age = _age
}
val player3 = Player3("Jacky",20)
println("name=${player3.name} age=${player3.age}")
//结果
name=Jacky age=20

嗯,这个跟Java蛮像的,只是主构造函数括号直接写在了类名之后,不像Java那样还需要单独去写而已。
在Kotlin中允许你不使用临时变量赋值,而是直接用一个定义童锁指定参数和类属性。通常,我们更喜欢用这种方式定义类属性,因为他会减少重复代码。

class Player4(var name:String,var age:Int){

}
val player4 = Player4("Jack",18)
println("name=${player4.name} age=${player4.age}")
//结果
name=Jack age=18

是不是感觉很干净,连赋值都免了。

2.2 次构造函数
有主则有次,与Java一样,我们也可以为类定义次构造函数

class Player4(var name:String,var age:Int){
    constructor(name:String):this(name,age = 20)
}
val player4 = Player4("Lily")
println("name=${player4.name} age=${player4.age}")
//结果
name=Lily age=20

同时,这个次构造函数还可以定义初始化代码逻辑

class Player4(var name:String,var age:Int){
    constructor(name:String):this(name,age = 20){
        this.name = name.toUpperCase()
    }
}
//结果
name=LILY age=20

有点东西,感觉真的很灵活。写法也与Java差不太多。
2.3 默认参数

class Player5(var name:String = "Jack",var age:Int = 20){

}
val player5 = Player5()
println("name=${player5.name} age=${player5.age}")
//结果
name=Jack age=20

好吧,不多啰嗦,与Java比只是初始化值写在哪的问题吧。
2.4 初始化块
初始化块可以设置变量或值,执行检查有效性。

class Player5(var name:String = "Jack",var age:Int = 20){
    init {
        require(age > 0){"age must more than zero"}
    }
}
val player5 = Player5(age = -1)
println("name=${player5.name} age=${player5.age}")

Exception in thread "main" java.lang.IllegalArgumentException: age must more than zero
	at Player5.<init>(InitTest.kt:13)
	at Player5.<init>(InitTest.kt:11)
	at InitTestKt.main(InitTest.kt:8)
	at InitTestKt.main(InitTest.kt)

在这里我们在初始化块里对参数执行了检查,如果不符合则抛出了异常,试着体会下吧。
对类的初始化Kotlin依照如下优先级进行执行:
a、主构造函数里声明的属性
b、类级别的属性赋值
c、init初始化块里的属性赋值和函数调用
d、次构造函数里的属性赋值和函数调用
2.5 延迟初始化
使用lateinit关键字相当于做了一个约定:在使用它之前负责初始化
如果无法确认lateinit变量是否完成初始化,可以调用isInitialized进行检查

class Player6{
    lateinit var nickName:String
    fun ready(){
        nickName = "pig"
    }
    fun use(){
        if(::nickName.isInitialized) println(nickName)
    }
}
val player6 = Player6()
player6.ready()
//如果未调用初始化就直接调用,不会有任何输出,因为属性还未初始化
player6.use()

2.6 惰性初始化
延迟初始化并不是推后初始化的唯一方式,你也可以暂时不初始化某个变量,直到首次使用它才进行初始化

class Player7{
    val name:String by lazy {
        println("init name")
        "Jack"
    }
}
val player7 = Player7()
println(player7.name)
println(player7.name)
//结果
init name
Jack
Jack

注意:惰性初始化的属性一定需要是只读属性,它的初始化工作会在第一次调用它的时候发生,且只发生一次。 en… 请结合代码与结果细品吧。

三 继承
3.1 在Kotlin类默认都是封闭的,是无法被其它类所继承的。如果需要让类能够被继承,那么需要是用open关键字去修饰。

class Player8: Player7() {

}
//如果这个类没有被open关键字修饰是无法被其它类继承的
open class Player7{
    val name:String by lazy {
        println("init name")
        "Jack"
    }
}

3.2 函数重载
父类的函数如果要支持被重载,那么也需要使用open关键字修饰,子类才能覆盖它。

class Player8: Player7() {
    override fun sayHello() {
        println("Hello,$name")
    }
}
//如果这个类没有被open关键字修饰是无法被其它类继承的
open class Player7{
    val name:String by lazy {
        println("init name")
        "Jack"
    }
    //没有open关键字则子类无法重载该函数
    open fun sayHello(){
        println("${name}, Hello")
    }
}

3.3 类型检测
Kotlin的is运算符可以用来检查某个对象类型

val player7 = Player7()
println(player7 is Player7)
println(player7 is Player8)

3.4 类型转换
as操作符可以进行类型转换

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值