Kotlin学习5
继承与重载的open关键字
类默认都是封闭(final)的,要让某个类开放继承,必须使用open关键字修饰他
open class Product(val name:String){
fun des() = "Product: $name"
//父类的函数也要以open关键字修饰,子类才能覆盖它
open fun load() ="nothing..."
}
class LuxProduct :Product("Luxury"){
override fun load() = "luxProduct loading..."//父类方法没用open会报错
}
fun main(){
val p :Product = LuxuryProduct()
println(p.load())
}
类型转换(as关键字)
kotlin的is关键字用来检测某个对象的类型
open class Product(val name:String){
fun des() = "Product: $name"
//父类的函数也要以open关键字修饰,子类才能覆盖它
open fun load() ="nothing..."
}
class LuxProduct :Product("Luxury"){
override fun load() = "luxProduct loading..."//父类方法没用open会报错
fun special() = "LuxProduct special"
}
fun main(){
val p= LuxuryProduct()
println(p is LuxuryProduct) //true
println(p is Product) //true
//as 关键字
if(p is LuxuryProduct){
println((p as LuxuryProduct).special())
}
}
智能类型转换
kotlin编译器,只要确定any is 父类条件检查属实,他就会将any当做子类类型对待,因此允许你不经类型转换直接使用
println((p as LuxuryProduct).special())//转一次就不需要再转了
println(p.special())
Any超类
kotlin共同继承一个Any超类,java里的Object类
println(p is Any) //true
object关键字
使用object关键字,可以定义一个只能产生一个实例的类--单例
使用object关键字有三种方式
对象声明
对象表达式
伴生对象
对象声明
对象声明有利于组织代码和管理状态,尤其是管理整个应用运行生命周期内的某些一致性状态
object ApplicationConfig{
init{
println("loading config ...")
}
fun setSomething(){
println("setSongthing")
}
}
fun main(){
//ApplicationConfig即是类名又是实例名字
ApplicationConfig.setSomething()
pringln(ApplicationConfig)
pringln(ApplicationConfig)
}
对象表达式
有时候不一定非要定义一个新的命名类不可,也许你需要某个现有类的一种变体实例,但只需要一次就行了,事实上,对于这种用完就丢的类实例,连命名都可以省了。这个对象表达式是xx的子类,这个匿名类依然遵循object关键字的一个规则,即一旦实例化,该匿名类只能有唯一一个实例存在
open class Player{
open fun load() = "loading nothing."
}
fun main(){
val p = object:Player(){
override fun load()="anonymous class load..."
}
println (p.load())
}
伴生对象
如果你想将某个对象的初始化和一个类实例捆绑在一起,可以考虑使用伴生对象,使用companion修饰符,你可以在一个类定义里声明一个伴生对象,一个类只能有一个伴生对象。
import java.io.File
open class ConfigMap{
//只有初始化ConfigMap类或调用load函数时,伴生对象的内容才会载入
//而且无论实例化多少次,这个伴生对象只能有一个实例存在
companion object{
private const val PATH = "xxx"
fun load() =FIle(PATH).readBytes()
}
}
fun main(){
//static
ConfigMap.load()
}
嵌套类
一个类只对另一个类有用,那么将其嵌入到该类中并使这两个类保持在一起。
class Player2(){
class Equipment(var name:String){
fun show() = printin("$name")
}
}
fun main(){
//这里Player2.Equipment()就可以了
Player2.Equipment("AK47").show()
}
数据类
专门设计用来存储数据的类
数据类型提供了toString的个性化实现
==符号默认是比较引用值,数据类型提供了equals和hashCode的个性化实现
data class Coordinate(var x:Int,var y:Int){
val isInBounds = x>=0 && y>=0
}
fun main (){
println(Coordinate(1,5)) //Coordinate(x=1,y=5)
//==比较的是内容,equals 默认是===
//===比较的是引用
println(Coordinate(1,5)==Coordinate(1,5))//true 如果Coordinate类不加data是false
}
copy函数
方便的复制一个对象。
data class Student(var name:String ,val age :Int){
var score = 10;
private val hobby = "music"
val subject :String
init{
println("init student")
subject = "math"
}
constructor(_name:String):this(_name,10){
score = 20
}
override fun toString ():String{
return "Student(name='$name', age = $age,"+
"score=$score, hobby=' $hobby',"+
"subject='$subject')"
}
}
fun main(args:Array<String>){
val s = Student("jack")
val copy = s.copy("Rose")
println(copy)//Student(name = 'Rose',age = 10,...)
}
copy函数有个小坑:次构造函数里如果对某个属性作了更改,新的copy对象是没有改的!!!!
解构声明
解构声明的后台实现就是声明component1、component2等若干个组件函数,让每个函数负责管理你想反回的一个属性数据,如果你定义一个数据类,他会自动为所有定义在主构造函数的属性添加对应的组建函数
class PlayerScore(val exp:Int , val level:Int){
//固定是component1和component2
operator fun component1() = exp
operator fun component2() = level
}
fun main(){
val (x,y) = PlayerScore(10,20)
}
数据类直接支持解构语法
运算符重载
13.pngdata class Coordinate2(var x:Int,var y :Int){
val isInBounds = x>=0&&y>=0
operator fun plus(other:Coordinate2)=
Coordinate2(x +other.x,y+other.y)
}
fun main(){
val c1 = Coordinate2(5,6)
val c2 = Coordinate2(10,20)
println(c1+c2)
}
枚举类
enum class Dir{
EAST,
WEST,
SOUTH,
NORTH
}
fun main(){
println(Dir.EAST)//EAST
}
枚举类定义函数
//给枚举类添加一个柱构造函数
enum class Dir2(private val coordinate:Coordinate){
//因为枚举类的构造函数都带参数,所以定义每个枚举类常量时
//都要传入Coordinate对象,调用构造函数
EAST(Coordinate(5,-1)),
WEST(Coordinate(1,0)),
SOUTH(Coordinate(0,1)),
NORTH(Coordinate(-1,0));
fun updateCoordinate(playerC:Coordinate) =
Coordinate(playerC.x+coordinate.x,
playerC.y+coordinate.y)
}
fun main(){
pringln(DIr2.EAST.updateCoordinate(Coordinate(10,20)))
}
代数数据类型
可以用来标识一组子类型的闭集,枚举类就是一种简单的ADT
enum class Liscen{
UNQUALIFIED,
LEARNING,
QUALIFIED;
}
class Drive(var status :Liscen){
fun check() :String{
//不使用else,且编译器会帮你检查遗漏
return when (status){
Liscen.UNQUALIFIED->"没资格"
Liscen.LEARNING->"在学"
Liscen.QUALIFIED->"有资格"
}
}
}
密封类(sealed class)
密封类实现更复杂的ADT。
密封类可以有若干个子类,要继承密封类,这些子类必须和他定义在同一个文件里
sealed class Lic2{
object UnQualified :Lic2()
object Learning:Lic2()
class Qualified(val licenseId:String):Lic2()
}
class Drive2(var status:Lic2){
fun check():String{
return when(status){
is Lic2.UnQualified ->"没资格"
is Lic2.Learning ->"在学"
is Lic2.Qualified ->"有资格,编号:"+
"${(this.status as Lic2.Qualified).licenseId}"
}
}
}
fun main(){
val status = Lic2.Learning
val driver = Driver2(status)
println(driver.check())
val status2 = Lic2.Qualified("12312")
val driver2 = Driver2(status)
println(driver2.check())
}