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

[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源码直接运行,包含全部详细笔记

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

推荐阅读更多精彩内容