Kotlin学习笔记(六)

目录

继承

方法重写和重载

类型检测

类型转换

Any

Object

 嵌套类

数据类


继承

Kotlin的类默认都是封闭的,要让某个类开放继承,必须使用open关键字修饰它。

open class Product(val name: String) {}

class LuxuryProduct(name: String, private val price:Double):Product(name){}

方法重写和重载

父类的函数也要通过open关键字修饰才可以被子类重写。

open class Product(val name: String) {
    fun description() = "Product:$name"

    //方法重载
    open fun load() {
        "Nothing..."
    }

    open fun load(name: String) {
        println("$name is very expensive")
    }
}

class LuxuryProduct(name: String, private val price: Double) : Product(name) {
    //方法重写
    override fun load() {
        super.load()
        println("${super.description()},Price:$price")
    }

}

fun main() {
    val luxuryProduct: Product = LuxuryProduct("LV", 19800.00)
    println(luxuryProduct.description())
    luxuryProduct.load()
    luxuryProduct.load("LV")
}

类型检测

Kotlin的is运算符是一个不错的工具,可以用来检查某个对象的类型,其实就相当于Java中的instanceof关键字。

    println(luxuryProduct is Product)
    println(luxuryProduct is LuxuryProduct)
    println(luxuryProduct is File)

类型转换

通过as操作符完成类型转换,而且转换一次,后面再使用时无需转换。

open class Product0(val name: String) {
    fun special() = "Product0 special function"
}

class LuxuryProduct0 : Product0("Luxury") {}

fun main() {
    val lp = LuxuryProduct0()
    println((lp as Product0).special())
    //只需要转换一次,后面无需转换,也不需要用临时变量去存
    println(lp.special())
    println(lp.special())
}

 Kotlin编译器很聪明,只要能确定any is父类条件检查属实,就会将any当做子类类型对待,因此编译器允许我们不经类型转换直接使用。

open class Product2(val name: String) {
    fun load() {
        println(name)
    }
}

fun sale(p: Product2) {
    p.load()
}

class LuxuryProduct2(name: String, private val price: Double) : Product2(name) {}

fun main() {
    val lp2 = LuxuryProduct2("LV", 19800.00)
    //传入的参数是Product2的子类对象lp2
    //sale(lp2 as Product2)
    sale(lp2)
}

Any

无须在代码里显示指定,每一个类都会继承一个共同的叫做Any的超类。

Object

使用Object关键字,我们可以定义一个只能产生一个实例的类 ———— 单例。

使用Object有三种方式:对象声明、对象表达式、伴生对象。

对象声明

object ApplicationConfig{
    init {
        println("ApplicationConfig loading...")
    }

    fun doSomething(){
        println("doSomething")
    }
}

fun main() {
    //ApplicationConfig既是类名,也是实例名
    ApplicationConfig.doSomething()
    println(ApplicationConfig)
    println(ApplicationConfig)
}

 对象表达式

有时候我们不一定非要定义一个新的命名类不可,也许需要某个现有类的一种变体实例,但只需用一次就行了,事实上,对于这种用完就丢的类实例,连命名都可以省了。这个对象表达式是某个类的子类,这个匿名类依然遵循object关键字的一个原则,即一旦实例化,该匿名类只能有唯一一个实例存在。

open class Player {
    open fun load() = "loading nothing.."
}

fun main() {
    val p = object : Player() {
        override fun load() = "anonymous nothing.."
    }

    println(p.load())
}

 伴生对象

如果你想将某个对象的初始化和一个类实例捆绑在一起,可以考虑使用伴生对象,使用companion修饰符,可以在一个类定义里声明一个伴生对象,一个类只能有一个伴生对象。跟Java中的static有一些类似。

open class ConfigMap{

    companion object{
        private const val PATH= "d://honey.txt"

        fun load() = File(PATH).readBytes()
    }

}

fun main() {
    //static
    ConfigMap.load()
}

通过反编译,我们发现这基本就是Java中的static在Kotlin中的实现:

 嵌套类

如果一个类只对另一个类有用,那么将其嵌入到该类中并使用这两个类保持在一起是合乎逻辑的 ,可以使用嵌套类。

class Player2 {
    class Equipment(var name: String) {
        fun show() = println("equipment:$name")
    }
}

fun main() {
    Player2.Equipment("98k").show()
}

数据类

顾名思义,数据类是专门设计用来存储数据的类。

  • 数据类提供了toString的个性化实现
  • ==符号默认情况下,比较对象就是比较它们的引用值,数据类提供了equals和hashCode的个性化实现
data class Coordinate(var x: Int, var y: Int) {
    val isInBounds = x > 0 && y > 0
}

fun main() {
    println(Coordinate(10, 20))
    // == 比较的是内容,equals,Any 默认实现===,比较引用
    // === 比较的是引用
    println(Coordinate(10, 20) == Coordinate(10, 20))

    val (x, y) = Coordinate(10, 20)
    println("$x, $y")
}

数据类对toString、hashCode和equals方法的个性化实现:

copy函数

数据类除了重写Any类的部分函数,提供更好用的默认实现外,还提供了copy函数,用来复制一个对象。假设你想创建一个Student实例,除了name属性,它拥有和另一个现有Student实例完全一样的属性,如果Student是个数据类,那么复制现有Student就很简单,只要调用copy函数,给要修改的属性传入值参即可。

data class Student(var name: String, var age: Int) {
    var score = 0
    private val hobby = "music"
    private val subject: String

    init {
        println("initializing student")
        subject = "math"
    }

    constructor(_name: String) : this(_name, 16) {
        score = 98
    }

    override fun toString(): String {
        return "Student(name='$name', age=$age, score=$score, hobby='$hobby', subject='$subject')"
    }
}

fun main() {
    //使用主构造器创建对象
    val honey = Student("Honey", 15)
    val luffy = honey.copy("luffy")
    println(honey)
    println(luffy)

    //使用次构造器创建对象
    val honey2 = Student("Honey")
    val luffy2 = honey.copy("luffy")
    println(honey2)
    println(luffy2)

}

 从执行结果来看,copy函数其实就是创建一个跟原始对象一样的对象,只是它是通过主构造器实现的。

 解构声明

解构声明的实现就是声明若干个component1、component2这样的组件函数,让每个函数负责管理属性数据,如果我们定义的是数据类,Kotlin会自动为所有定义在主构造器的属性添加对应的组件函数。

 我们也可以通过operator关键字让普通类实现解构声明:

class Student2(var name: String, var age: Int) {
    operator fun component1() = name
    operator fun component2() = age
}

fun main() {
    val (name, age) = Student2("Honey", 17)
    println("$name,$age")
}

正是因为上述特性,我们才倾向于用数据类来表示存储数据的简单对象,对于那些经常需要比较、复制或者打印自身内容的类,数据类尤为适合。一个类要成为数据类,需要符合以下三个条件:

  • 数据类的主构造器必须至少带有一个参数
  • 数据类的主构造器的参数必须是val/var
  • 数据类不能使用abstract、open、sealed和inner修饰

这一讲先讲这么多,欲知后事如何,且听下回分说~欢迎大家评论加点赞,大伙的支持对我很重要哦(≧◉◡◉≦)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值