Kotlin语言(五):类和对象

1、面向对象

/**
 * 任何复杂的数据类型都是由基本数据类型组成的
 * 面向对象最重要的特点:用基本的数据类型描述复杂的事物
 *
 * 1.定义对象用class关键字
 * 2.对象一般都有静态属性和动态行为,但不是必须具备
 */

class Rect {}

class Boy {
    //静态属性
    var name: String = "张三"
    var age: Int = 20
}

class Girl {
    //动态行为
    fun greeting() {
        println("你好!")
    }
}

class People {
    //静态属性
    var name: String = "王五"
    var age: Int = 20

    //动态行为
    fun greeting() {
        println("你好!")
    }
}

fun main() {
    val rect = Rect()
    val boy = Boy()
    val girl = Girl()
    val people = People()
    println(people.name)
    println(people.age)
    people.greeting()
}

2、属性的get和set

/**
 * 1、通过反编译成java代码,发现kotlin的字段都是私有的,并且会自动生成get和set方法
 */
class Person0 {
    var name = "张三"
    var age = 18
}

/**
 * 2、修改get和set的可见性
 */
class Person1 {
    var name = "张三"
        private set//把name的set方法私有化,即name只能访问不能修改

    var age = 18
    val sex = true
}

/**
 * 3、自定义get和set访问器
 * 注:这里要注意 field 字段的使用,直接赋值或者获取值其实是调用的set和get方法,因而会进入无限递归调用
 */
class Person2 {
    var name = "张三"
    var age = 18
        set(value) {
            if (value in 0..150) {
                //age = value //这里直接赋值其实是调用的set方法,所以会进入无限递归调用自己
                field = value
            } else {
                field = 0
            }
            //field = if (value in 0..150) value else 0 //if表达式
        }
}

fun main() {
    val person0 = Person0()
    println("name=${person0.name}  age=${person0.age}") //name=张三  age=18

    val person1 = Person1()
    //person1.name = "李四" //编译报错,name的set方法私有化了,故不能修改其值
    //person1.sex = false   //sex为不可变变量,故不能修改,但是可以获取
    val sex = person1.sex

    val person2 = Person2()
    person2.age = 200
    println("age=${person2.age}") //age=0,赋值超过了0-100,故设置为0
}

3、构造函数

/**
 * 1、主构造函数
 * (1)定义方式:直接跟在类名后面
 * (2)实现代码:主要构造函数的实现是在 init 关键字包含的代码块中
 */
class News0(title: String, content: String) {
    var title = ""
    var content = ""

    //主构造函数的实现部分
    init {
        this.title = title
        this.content = content
    }
}

/**
 * 2、主构造函数参数使用 var 和 val
 * (1)构造函数参数使用 var 或者 val 则会自动生成相应的成员变量
 * (2)使用 val 时只能在构造函数里面赋值,后面不能再修改了
 */
class News1(var title: String, var content: String)

class News2(var title: String, val content: String)

/**
 * 3、次构造函数
 * (1)次构造函数使用 constructor 在类里面声明
 * (2)次构造函数后面可以使用 this 关键字调用其他次构造函数或主构造函数
 */
class News3(var title: String, val content: String) {
    var author = ""

    constructor(title: String, content: String, author: String) : this(title, content) {
        this.author = author
    }
}

/**
 * 4、构造函数执行顺序:先执行主构函数的 init,再执行次构函数
 */
class News4(title: String, content: String) {
    var title = ""
    var content = ""
    var author = ""
    var date = ""

    //构造函数的实现部分
    init {
        this.title = title
        this.content = content
        println("init执行!")
    }

    constructor(title: String, content: String, author: String) : this(title, content) {
        this.author = author
        println("次构造函数执行!")
    }

    constructor(title: String, content: String, author: String, date: String) : this(
        title,
        content,
        author
    ) {
        this.date = date
        println("次构造函数2执行!")
    }

}


fun main() {
    val news0 = News0("王者荣耀", "S18赛季更新。")
    println("news0 [title=${news0.title}, content=${news0.content}]")
    //news0 [title=王者荣耀, content=S18赛季更新]

    val news1 = News1("QQ飞车", "11城来赛一场啊!")
    println("news1 [title=${news1.title}, content=${news1.content}]")
    //news1 [title=QQ飞车, content=11城来赛一场啊!]
    val news2 = News2("QQ飞车", "11城来赛一场啊!")
    news2.title = "泡泡卡丁车" //编译通过,var变量可以修改
    //news2.content = "中国龙赛场战一场!" //编译失败,val变量不能修改

    val news3 = News3("欢乐斗地主", "来血战到底啊!", "腾讯")
    println("news3 [title=${news3.title}, content=${news3.content}, author=${news3.author}]")
    //news3 [title=欢乐斗地主, content=来血战到底啊!, author=腾讯]

    val news4 = News4("皮皮麻将", "刮风、下雨、碰碰碰!", "腾讯")
    println("news4 [title=${news4.title}, content=${news4.content}, author=${news4.author}]")
    //init执行!
    //次构造函数执行!
    //news4 [title=皮皮麻将, content=刮风、下雨、碰碰碰!, author=腾讯]

    val news5 = News4("皮皮麻将", "刮风、下雨、碰碰碰!", "腾讯", "2020-12-20")
    println("news5 [title=${news5.title}, content=${news5.content}, author=${news5.author}, date=${news5.date}]")
    //init执行!
    //次构造函数执行!
    //次构造函数2执行!
    //news5 [title=皮皮麻将, content=刮风、下雨、碰碰碰!, author=腾讯]
}

4、封装

/**
 * 所谓封装就是隐藏内部实现的细节,暴露一些安全的接口给外部;
 * 与 Java 一样 kotlin 也是通过 private 关键字来隐藏内部的实现细节
 */
class WashMachine(val brand: String, val capacity: Float) {

    // 这里的是否关门就是封装,人们不能直接来修改这个属性
    // 只能通过洗衣机提供的 openDoor() 和 closeDoor() 来控制
    private var isOpenDoor: Boolean = false

    fun openDoor() {
        isOpenDoor = true
        println("洗衣机门打开了")
    }

    fun closeDoor() {
        isOpenDoor = false
        println("洗衣机门关闭了")
    }

    fun startWash() {
        if (isOpenDoor){
            println("警告:没有关门,不能开始洗衣服!")
        } else {
            println("放水 -> 开始洗衣服 -> 脱水烘干 -> 衣服洗好了")
        }
    }
}


fun main() {
    val washMachine = WashMachine("海尔", 12.0f)
    washMachine.openDoor() //洗衣机门打开了
    println("放衣服和臭袜子,倒入洗衣液")
    washMachine.startWash() //警告:没有关门,不能开始洗衣服!

    washMachine.closeDoor() //洗衣机门关闭了
    washMachine.startWash() //放水 -> 开始洗衣服 -> 脱水烘干 -> 衣服洗好了
}

5、继承

/**
 * 1、类的继承
 * (1)kotlin 的 class 默认都是 final 类型的,不能被继承
 * (2)只有被 open 关键字修饰的类才可以被继承(关键字 open 的作用与 final 相反)
 * (3)kotlin 的 class 只能单继承
 */
open class Parent0

class Child0 : Parent0()


/**
 * 2、初始化父类的构造函数
 * (1)子类有主构造函数,则直接在主构造函数后面初始化父类的构造函数
 * (2)子类没有主构造函数,则需要使用 super 关键字或者调用其他的次构造函数来初始化父类的构造函数
 */
open class Parent1 {
    constructor()
    constructor(name: String)
    constructor(name: String, age: Int) : this(name)
}

class Child1_0 : Parent1() //class未定义构造函数时会有一个默认的构造函数,此处为Child1_0()
class Child1_1(name: String) : Parent1(name)
class Child1_2(name: String, age: Int) : Parent1(name, age)
class Child1_3 : Parent1 {
    constructor() : super()
    constructor(name: String) : super(name)
    constructor(name: String, age: Int) : super(name, age)
    constructor(name: String, age: Int, sex: Boolean) : this(name, age)
}


/**
 * 3、方法的重写
 * (1)kotlin 的 class 中的方法默认也是 final 类型,不能被重写
 * (2)只有被 open 关键字修饰的方法才能被重写
 * (3)重写父类中 open 修饰的方法,需在子类的方法前面加 override 关键字
 * (4)若在父类中方法 override 前再加上 final 后,子类中的该方法是不能被重写的
 */
open class Parent2 {
    open fun todo() {
        println("Parent2的todo方法。")
    }
}

open class Child2_0 : Parent2() {
    final override fun todo() {
        super.todo()
        println("Child20的todo方法。")
    }
}

class Child2_1 : Child2_0() {
    //编译报错,final 方法不能被重写
    /*override fun todo() {
        super.todo()
    }*/
}


/**
 * 4、属性的重写
 * (1)只有被 open 关键字修饰的属性才能被重写
 * (2)var 属性只能被 var 属性重写
 * (3)val 既可以被 var 属性重写,也可以被val属性重写
 * (4)本质上,val 相当于 get 方法,var 相当于 get 和 set 方法
 */
open class Parent3{
    open var name: String = "Parent3"
    open val nickName: String = "Parent3_nick"
    open val age: Int = 0
    var sex:Boolean = false
}

class Child3_0:Parent3(){
    override var name: String = "Child3_0"
    //override val name: String = "" //编译失败,var 不能重写成 val
    override val nickName: String = "Child3_0_nick"
    override var age: Int = 10
    //override var sex: Boolean = true //编译失败,非 open 属性不能重写
}

6、抽象类

/**
 * 1、抽象类的定义和特点
 * (1)抽象类反映的是事物的本质
 * (2)使用 abstract 表示抽象类、抽象属性和抽象方法
 * (3)抽象类不能被实例化,即不能创建抽象类的对象
 * (4)抽象类可以有普通的方法和属性
 * (5)抽象类、抽象属性和抽象方法默认都是 open 的
 */
abstract class Human {
    abstract var color: String     //抽象属性
    abstract var language: String  //抽象属性
    open val sleep: Boolean = true //普通属性

    abstract fun eat() //抽象方法
    fun walk() {       //普通方法
        println("人类都是用双脚走路!")
    }
}


/**
 * 2、抽象类的继承
 * (1)抽象类只能单继承
 * (2)抽象类也可以继承抽象类
 * (3)继承一个抽象类必须实现其所有的抽象方法和属性,否则子类也必须声明为抽象类
 */
class ZhHuman : Human() {
    override var color: String = "黄皮肤"
    override var language: String = "中文"

    override fun eat() {
        println("中国人用筷子吃饭!")
    }
}

abstract class UsHuman : Human() {
    //美国人的肤色很多,这里也不能确定,所以不实现 color,将类声明为抽象类
    //override var color: String = "白皮肤"
    override var language: String = "英语"

    override fun eat() {
        println("美国人用刀叉吃饭!")
    }
}


fun main() {
    val zhHuman = ZhHuman()
    println(zhHuman.color)    //黄皮肤
    println(zhHuman.language) //中文
    zhHuman.eat()             //中国人用筷子吃饭!
    zhHuman.walk()            //人类都是用双脚走路!

    //val usHuman = UsHuman() //编译错误,抽象类不能实例化
}

7、接口

/**
 * 1、接口的定义和特点
 * (1)接口反映的是事物的能力
 * (2)使用 interface 来定义接口
 * (3)接口不能实例化,即不能创建接口对象
 * (4)kotlin 接口里面的字段不能实现(java 里面可以实现)
 * (5)kotlin 接口里面的方法可以实现(java 里面不能实现)
 */
//骑自行车
interface RideBike {
    fun ride()
}

//开车
interface DriveCar {
    var license: String //驾照号码
    fun drive() {
        println("挂挡 -> 踩油门 -> 走你")
    }
}


/**
 * 2、接口的实现
 * (1)接口也可以继承接口,添加新的能力
 * (2)一个类可以实现多个接口,用逗号隔开
 * (3)实现接口必须实现接口定义的所有未实现的方法和属性
 */
//开船
interface Sail : DriveCar {
    fun course() //航向
}

class XiaoMing : RideBike, Sail {

    //小明的驾照号码是123456789
    override var license: String = "123456789"

    override fun ride() {
        println("小明学会了骑自行车!")
    }

    override fun course() {
        println("一路向北!")
    }
}


fun main() {
    val xiaoMing = XiaoMing()
    xiaoMing.ride()   //小明学会了骑自行车!
    xiaoMing.drive()  //挂挡 -> 踩油门 -> 走你
    xiaoMing.course() //一路向北!
    println(xiaoMing.license) //123456789
}

8、多态

/**
 * 1.多态
 * (1)多态即同一类对象对同一种功能具有不同的表现形式
 * (2)方法重写:指向父类的对象调用此方法,其实执行的是子类重写的方法
 * (2)方法重载:由于父类没有此方法,故指向父类的对象不能调用此方法
 */
open class Animal {
    open fun call() {
        println("动物叫声")
    }
}

class Cat : Animal() {
    override fun call() {
        println("猫喵喵叫")
    }
}

class Duck : Animal() {
    fun call(isBig: Boolean) {
        println("鸭子呱呱叫")
    }
}

fun main() {
    val animal1: Animal = Cat()
    val animal2: Animal = Duck()
    animal1.call() //执行的是子类重写的方法,输出:猫喵喵叫
    animal2.call() //子类没有重写,执行的是父类的方法,输出:动物叫声
    //animal2.call(true) //编译失败,Animal没有这个方法,故不能调用
}

9、智能类型转换

/**
 * 1、is 关键字:用来判断某个对象是否是某个具体的类型
 * 2、as 关键字:用来将某个对象强转成某个类型,如果类型转换错误会抛出 ClassCastException
 * 3、智能类型转换:用is判断之后就不需要再用 as 显示转换,编译器会自动转换
 */
open class Canine

class ShepHerdDog : Canine() {
    fun herdShep() {
        println("牧羊犬放羊")
    }
}

class RuralDog : Canine() {
    fun watchDoor() {
        println("中华田园犬看家")
    }
}

fun main() {
    val canine1: Canine = ShepHerdDog()
    val canine2: Canine = RuralDog()

    if(canine1 is ShepHerdDog){
        //is判断了,就不需要再用as显示转换
        //val shepHerdDog = canine1 as ShepHerdDog
        canine1.herdShep() //牧羊犬放羊
    }

    if(canine2 is RuralDog){
        canine2.watchDoor() //中华田园犬看家
    }

    //如果类型转换错误会抛出ClassCastException
    val shepHerdDog = canine1 as ShepHerdDog
    shepHerdDog.herdShep() //牧羊犬放羊
    val ruralDog = canine2 as RuralDog
    ruralDog.watchDoor()   //中华田园犬看家
}

10、嵌套类

/**
 * 1、嵌套类
 * (1)嵌套类是静态类
 * (2)嵌套类和外部类没有关系,不能访问外部类的成员
 * (3)嵌套类可以直接通过构造函数创建对象
 */
class OutClass {
    var content = "嵌套类"

    class NestedClass {
        fun sayHello() {
            //println("hello $content") //编译失败
            println("Hello 嵌套类")
        }
    }
}

/**
 * 2、内部类
 * (1)内部类用关键字 inner 声明
 * (2)内部类是普通类
 * (3)可以把它看成是外部类的一个成员,它可以访问外部类的成员
 * (4)内部类的对象必须要通过外部类的对象来创建
 * (5)内部类访问外部类的同名成员需要使用 this@OutClass2 的形式调用
 */
class OutClass2 {
    var content = "外部类"

    inner class InnerClass {
        var content = "内部类"
        fun sayHello1() {
            println("Hello $content")

        }

        fun sayHello2() {
            println("Hello ${this@OutClass2.content}")
        }
    }
}


fun main() {
    val nestedClass = OutClass.NestedClass()
    nestedClass.sayHello() //Hello 嵌套类

    val innerClass = OutClass2().InnerClass()
    innerClass.sayHello1() //Hello 内部类
    innerClass.sayHello2() //Hello 外部类
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容