美文网首页kotlin
Kotlin学习笔记(四)-面向对象

Kotlin学习笔记(四)-面向对象

作者: g小志 | 来源:发表于2019-12-17 11:00 被阅读0次

    [toc]

    前言

    本章将讲解一些管对对象和基础编程的知识。如:类,接口,抽象类,数据类,扩展方法等

    接口

    其实这部分的设计思路和Java基本一致。这里主要说下Kotlin特有的属性

    abstract class Manager : Driver, Writer {
        override fun driver() {
        }
    
        override fun writer() {
        }
    
    }
    
    interface Driver {
        fun driver(){
            println("driver")
        }
    }
    
    class CarDriver : Driver {
        override fun driver() {
            println("开车")//接口的默认实现
        }
    
    }
    
    interface Writer {
        fun writer()
    }
    
    class PPTWriter : Writer {
        override fun writer() {
            println("写PPT")
        }
    
    }
    
    class SeniorManager(val driver: Driver, val writer: Writer) : Driver by driver, Writer by writer {//实现忌口但是通过by关键字可以不用去实现里面的接口方法
    
    
    }
    
    /**
     * 不加by关键字那么就要实现接口的方法
     */
    class SeniorManager1(val driver: Driver, val writer: Writer) : Driver, Writer {
        override fun driver() {
            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        }
    
        override fun writer() {
            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        }
    
    
    }
    
    fun main(args: Array<String>) {
        val driver = CarDriver()
        val writer = PPTWriter()
        val seniorManager = SeniorManager(driver, writer)
        seniorManager.driver()
        seniorManager.writer()
    }
    
    • interface代理用 by关键字,可以不用复写实现的interface的方法,同时不实现会执行by后面对象里的方法
    • interface中的抽象方法可以有默认实现。如果不去实现将执行默认方法
    抽象类

    接口是约定,抽象类本是是类的一种:

    abstract class Person(val age: Int) {
        open fun work() {
        }
    }
    
    abstract class Person1(open val age: Int = 3) {
    
        abstract fun work()
    }
    
    
    class MaNong(age: Int) : Person(age) {
        override fun work() {
            super.work()
            println("我是码农,我在写代码")
        }
    }
    
    class Doctor(age: Int) : Person1(age) {
        override val age: Int
            get() = 6//5.传入后又重写
    
        override fun work() {
            println("我是医生,我给人看病")
        }
    }
    
    1. 默认的类和方法都是final,如果想要被子类继承,需要加open关键字修饰;
    2. 子类覆写父类方法时需要加关键字override修饰
    3. 用open表示父类方法有默认的实现 子类可以有super.work 用abstract表示不可以有默认实现 他是一个抽象的无实现方法
    4. 不止可以重写方法方法,还可以重写属性
    5. 内部重写是优先级高于外部传入的
    可见性修饰符
    Kotlin Java
    private private
    protected↑ protected
    - default (包内可见)
    internal (模块Module内可见) -
    public public
    object类关键字
    • object修饰的类只有一个实例的类
    • 本质上就是单例模式最基本的实现(static代码块)
    • 不能自定义构造方法(因为是单例)
    • 可以实现接口、继承父类

    例子:

    interface Listener {
        fun start()
        fun stop()
    }
    
    abstract class Player
    
    object MusicPlayer : Player(), Listener {//可以实现接口,继承父类
        override fun start() {
        }
    
        override fun stop() {
        }
    
        val state = 0//可以有成员属性
    
        fun continuePlay() {//可以有方法
        }
    }
    
    fun main(args: Array<String>) {
        val music: MusicPlayer = MusicPlayer//后面没有括号,也就是说明不是调用构造方法创建的对象
    }
    
    伴生对象与静态成员

    kotlin 中时没有static 这种方法修饰的静态方法 所以要实现 类似于java中的静态属性就要用到伴生对象
    例子:

    fun main(args: Array<String>) {
        /**
         * 这时候调用伴生对象就相当于调用java静态方法 格式相同
         */
        val value = Latitude.ofDouble(3.0)
        println(Latitude.TAG)
    }
    
    /**
     * 私有的构造方法  companion伴生对象关键字
     */
    class Latitude private constructor(val latitude: Double) {
        companion object {
            fun ofDouble(double: Double): Latitude {
                return Latitude(double)
            }
    
            @JvmStatic//加上这个注解可以在Java中如static一样调用
            fun ofLatitude(latitude: Latitude): Latitude {
                return Latitude(latitude.latitude)
            }
    
            @JvmField//加上这个注解可以在Java中如static一样调用
            val TAG = "Latitude"
        }
    }
    
    • 每个类可以对应一个伴生对象
    • 伴生对象的成员全局独一份
    • 伴生对象的成员类似Java的静态成员
    • 静态成员考虑用包级函数、变量替代
    • JvmField 和JvmStatic使用后,可以用相同的语法在Java中调用
    方法重载

    与Java相同,,需要注意一下几点

    • 方法的重载与默认参数 返回值类型不能作为方法签名的一部分 只有参数列表和方法名
    • 重载时如果不能用默认参数解决的重载 不是一个好的设计 例如 list.remove
    • 默认参数 可以参数 不传参数用默认值 这个方法java要调用 需要加上 @JvmOverloads否则必须传参数
    扩展方法

    为现有类添加方法、属性:

    • fun X.y):Z{..}
    • val X.m 注意扩展属性不能初始化,类似接口属性
    • Java 调用扩展成员类似调用静态方法

    例子:

    
    //自己定义一个扩展方法 方法的名字是  类型.方法名
    fun String.copy(int: Int): String {
        val buildStr = StringBuilder()
        for (i in 0 until int) {
            buildStr.append("abc")
        }
        return buildStr.toString()
    
    }
    
    //也可以用操作符
    operator fun String.times(int: Int): String {
        val buildStr = StringBuilder()
        for (i in 0 until int) {
            buildStr.append("abc")
        }
        return buildStr.toString()
    
    }
    
    输出:
    
    fun main(args: Array<String>) {
        println("abc".copy(5))
        println("abc" * 5)//操作符 times对应* 具体参照文档 https://kotlinlang.org/docs/reference/operator-overloading.html
    }
    
    属性代理

    代理的场景 比如定义一个属性外界去访问,可以在getValue去读取一个文件setValue去写入一个文件那么就相当于与读取一个就可以文件,调用处代码变得非常简洁

    class Delegates4_9 {
        val hello by lazy {
            //懒加载 只有第一次使用的时候才回去初始化
            "helloWorld"
        }
    
        val hello2 by LazyX()//代理 //不可变代理 使用2处代码
        var hello3 by LazyX()//代理
    
    }
    
    class LazyX {
        var value: String? = null
    
        //仿写Lazy   2
    //    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
    //        return "Hello Lazy"
    //    }
    
        operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
            println("getValue: $thisRef ->${property.name}")
            return "Hello Lazy"
        }
    
        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
            println("setValue: $thisRef ->${property.name} = $value")
            this.value = value
        }
    }
    
    fun main(args: Array<String>) {
        val delegates = Delegates4_9()
        println(delegates.hello)
        println(delegates.hello2)
        println(delegates.hello3)
        delegates.hello3 = "value of hello3"
        println(delegates.hello3)
    }
    
    数据类

    主要是讲解data关键字,data主要是帮助生成copy,toString,componentN(对应返回定义的参数) hasCode,equals等方法,默认是没有无参数的构造方法并且生成的类是final的,需要用allOpen去掉final,noArg创建无参数构造函数
    allOpen/noArg

    appModule下build.gradle 
    apply plugin: 'kotlin-noarg'
    
    apply plugin: 'kotlin-allopen'
    
    noArg {
        annotation("packageName.Poko")
    }
    
    allOpen {
        annotation("packageName.Poko")
    }
    
    

    项目下的build.gradle:

     dependencies {
            classpath "org.jetbrains.kotlin:kotlin-noarg:1.3.21"
            classpath "org.jetbrains.kotlin:kotlin-allopen:1.3.21"
            // NOTE: Do not place your application dependencies here; they belong
            // in the individual module build.gradle files
        }
    

    使用:

    @Poko
    data class Country4_10(val id: Int, val name: String)
    
    内部类

    Kotlin的内部类与Java内部类概念相同。存在以下几种不同点:

    • Kotlin内部类定义时如果没有用inner修饰的话就是静态内部类,用inner修饰后是非静态内部类
    • 匿名内部类:
      1. 没有定义名字的内部类
      2. 类名编译时生成,类似0utter$1. class .
      3. 可继承父类,实现多个接口,与Java注意区别.
    • kotlin内部类与java 内部类的区别
      1. java的内部类可以直接访问外部类的成员, kotlin的内部类不能直接访问外部类的成员
      2. 必须用inner标记之后才能访问外部类的成员(非静态内部类持有外部类的引用,而静态内部类无法持有外部类的引用,这是因为静态内部类优先于非实例对象而存在)
    • 内部类和静态内部类的区别:
      1. 是否持有外部类的状态(也就是非静态内部类中可以调用 外部类.this.属性/方法 静态内部类做不到)
    • 匿名内部类和内部类的区别:
      1. 内部类没有定义名字直接new出来 但是在编译后也是有的类似0utter$1. class .

    例子:

    class Outter {
        val a = 0
    
        class Inner {
            fun main(args: Array<String>) {
    //            println(a)//访问不到 说明kotlin中默认是使用静态static内部类的
                println(Outter().a)
            }
        }
    
        inner class Inner1 {
            //inner关键字 变成非静态 这样就可以访问到外部类的属性的
            val a = 6
    
            fun main(args: Array<String>) {
    //            println(a)//访问不到 说明kotlin中默认是使用静态static内部类的
                println(this@Outter.a)//当内外部类重载相同的属性或方法时 通过this@Outter访问
                println(this@Inner1.a)
                println(a)
            }
        }
    }
    
    
    interface OnClickListener {
        fun click()
    }
    
    class View {
        var onClickListener: OnClickListener? = null
    }
    
    open class Text
    
    fun main(args: Array<String>) {
        val view = View()
        view.onClickListener = object : Text(), OnClickListener {
            //java 匿名内部类是不能继承的 kotlin可以
            //用object关键字来实例化内部类
            override fun click() {
    
            }
    
        }
    }
    
    枚举类

    基本与Java一致。例子:

    enum class LogcatLevel(val id: Int) {
    
        VERBOSE(5), DEBUG(6), INFO(7), WARN(8), ERROR(9), ASSERT(10);//枚举类可以定义方法
    
        fun getTag(): String {
            return "$name , $id"
        }
    
        override fun toString(): String {
            return "$name , $ordinal"
        }
    }
    

    Kotlin枚举类中定义方法,那么要在枚举对象最后加上; 这基本是Kotlin中唯一一个需要强制写;的地方

    密封类
    • 密封类与枚举的区别:前者是子类可数,后者是实例可数
    • 密封类(Sealed Class)的子类必须和父类定义在同一个文件中,或者作为它的内部类。
    • 密封类的子类是可数,因为子类只能在父类内部或者和父类处于同一个文件,在其他地方是无法创建子类的。这个可数的定义就是有限的 一目了然知道的

    结语

    记过这4篇文章的学习,基本已经掌握Kotlin的基本语法和对Java的对比,也顺带复习了一下Java的知识,下一篇我们来学习一些关于Kotlin的高阶函数

    Github源码直接运行,包含全部详细笔记

    相关文章

      网友评论

        本文标题:Kotlin学习笔记(四)-面向对象

        本文链接:https://www.haomeiwen.com/subject/hcksnctx.html