原创

Kotlin中的final和open修饰符

final关键字可用于修饰类、属性和方法,表示它所修饰的类、属性和方法不可改变。

Kotlin与JAVA的一个重大区别:

Kotlin的final修饰符不能修饰局部变量,open也不能修饰局部变量。

一、可执行“宏替换”的常量

Kotlin提供了const用来修饰可执行“宏替换”的常量,这种常量也被称为“编译时”常量,以为它在编译阶段就会被替换掉。

“宏替换”的常量除使用const修饰之外,还必须满足如下条件:

  • 位于顶层或者是对象表达式的成员
  • 初始值为基本类型值或字符串字面值
  • 没有自定义的getter方法
//定义支持“宏替换”的常量
const val MAX_AGE = 100

fun main(args: Array<String>) {
    println(MAX_AGE)
}

输出结果:

100

如果被赋值的表达式只是基本的算术表达式或进行字符串连接运算,没有访问普通变量、常量,调用方法,Kotlin编译器同样会将这种const常量当成“宏变量”处理。

//定义3个“宏变量”
const val a = 1+2
const val b:Double=1.2/3
const val str:String="疯狂"+"外星人"

fun main(args: Array<String>) {
    println(str === "疯狂外星人")
}

输出结果:

true

二、final属性

final属性表明该属性不能被重写,而且入股程序对属性不适用任何修饰符,Kotlin会自动为该属性添加final。

如果一个属性没有使用open修饰,Kotlin会自动为该属性添加final修饰,那么子类就不可以重写该属性。

三、final方法

使用final修饰的方法不可被重写。

如果程序不为方法添加任何修饰符,Kotlin会自动为该方法添加final修饰。

即使使用final修饰一个private访问权限的方法,也依然可以在其子类中定义与该方法具有相同方法名、相同形参列表、相同返回值类型的方法。

open class PrivateFinalMemberTest {
    private final fun test() {}
    private final var name: String = "父类属性"
}

class SubTest : PrivateFinalMemberTest() {
    public fun test() {}
    public var name: String = "子类属性"
}

使用final修饰的方法仅仅只是不能被重写,但可以被重载。

class FinalOverload{
    final fun test(){}
    final fun test(arg:String){}
}

四、final类

使用final修饰的类不可以有子类,如果一个类没有显式使用open修饰符修饰,Kotlin会自动为该类添加final修饰。

当子类继承父类时,将可以访问到父类内部数据,并可通过重写父类方法来改变其实现细节,这可能会导致一些不安全的因素。

五、不可变类

不可变类的意思是创建该类的实例后,该实例的属性值是不可改变的。

创建自定义的不可变类,可遵守如下规则:

  • 提供带参数的构造器,用于根据传入的参数来初始化类中的属性。
  • 定义使用final修饰的只读属性,避免程序通过setter方法改变该属性值。

如果有必要,则重写Any类的hashCode()和equals()方法。

class Address(val detail: String, val postCode: String) {
    override fun equals(other: Any?): Boolean {
        if (this == other) {
            return true
        }
        if (other == null) {
            return false
        }
        if (other.javaClass == Address::class) {
            var ad = other as Address
            return this.detail.equals(ad.detail) && this.postCode.equals(ad.postCode)
        }
        return false
    }

    override fun hashCode(): Int {
        return detail.hashCode()+postCode.hashCode()*31
    }
}

当创建不可变类时,如果它包含的成员属性的类型是可变的,那么其对象的属性值依然是可改变的。

class Name(var firstName: String = "", var lastName: String = "")

class Person(val name: Name) {
}

fun main(args: Array<String>) {
    val n=Name("悟空","孙")
    var p=Person(n)
    println(p.name.firstName)
    n.firstName="八戒"
    println(p.name.firstName)
}

输出结果:

悟空
八戒

为了保持Person对象的不可变性,必须保护好Person对象的引用类型的属性。

class Name(var firstName: String = "", var lastName: String = "")

class Person{
    val name:Name
    get() = Name(field.firstName,field.lastName)
    constructor(name: Name){
        this.name=Name(name.firstName,name.lastName)
    }
}

fun main(args: Array<String>) {
    val n=Name("悟空","孙")
    var p=Person(n)
    println(p.name.firstName)
    n.firstName="八戒"
    println(p.name.firstName)
}

输出结果:

悟空
悟空

在设计一个不可变类时要注意其引用类型的属性,如果属性的类型本身是可变的,就必须采取必要的措施来保护该属性所引用的对象不会被修改。

学海无涯苦作舟

我的微信公众号.jpg

基本语法
  • 作者:HunterArley (联系作者)
  • 发表时间:2019-11-26 10:20
  • 版权声明:本网站部分内容转载于合作站点或其他站点,但都会注明作/译者和原出处。如有不妥之处,敬请指出。
  • 公众号转载:请在文末添加作者公众号二维码