Kotlin 進階之路4 面向對象

Kotlin 進階之路 目錄

1.面向對向編程

  • 本質上就是解決如何用程序描述世界的問題
  • 討論如何把實際存在東西映射成程序的類和對象
  • 一種程序設計的思路、思想、方法
/**
 * 面向對向編程
 * 對某種事物進行抽象化,稱之為建模
 *
 */

//kotlin 定義:class 類名 constructor(屬性列表){更多的屬性和方法描述}
//構造器:用來設置類新實例的出廠配置

//類要被繼承需要加open
open class Chinese constructor(val sex: Boolean, var region: String) {
    //普通屬性,與變量定義相似
    open val skin = "yellow"

    //組合屬性,由其它屬性計算而來(get)
    val avgLife: Double
        get() {
            when (this.region) {
                    "Shanghai" -> {
                return 82.4
            }
                "Beijing" ->{
                    return 80.0
                }

                else -> {
                    return 72.1
                }
            }
        }
    //組合屬性反過來可以影響其它屬性(set,可選) this代表實例
    var avgSalary : Int
        get(){
            when(this.region){
                "Shanghai" ->{ return 4000}
                "Beijing" ->{ return 5100}
                else ->{ return 3500}
            }
        }
        set(value) {
            when (value) {
                in 4500..Int.MAX_VALUE -> {
                    this.region ="Beijing"
                }
                in 2800..4100->{
                    this.region ="Shanghai"

                }
                else -> {
                    this.region = "homeland"
                }
            }
        }

    //方法:廚藝
   open fun cook(): Unit {
        val meau = arrayOf("宮爆雞丁","番茄炒雞蛋","蔥爆牛肉")
        val desc = meau.reduce{s1,s2->s1+","+s2}
        println("我會${desc}")
    }
}

class BeijingPeople(sex: Boolean, region: String = "Beijing") : Chinese(sex, region) {

    val greatBuilding="the Forbidden City"
    //override覆蓋屬性好方法
    override val skin ="BeijingYellow"

    override fun cook() {
        super.cook()
        val meau = arrayOf("北京烤鴨","北京炸醬面","北京包子")
        val desc = meau.reduce{s1,s2->s1+","+s2}
        println("我還會${desc}")
    }
}

class ShanghaiPeople(sex: Boolean, region: String = "Shanghai") : Chinese(sex, region){
    val greatBuilding="the Oriental Pearl Tower"
    override val skin ="ShanghaiYellow"
    override fun cook() {
        super.cook()
        val meau = arrayOf("大閘蟹","小湯包","芝士焗面")
        val desc = meau.reduce{s1,s2->s1+","+s2}
        println("我還會${desc}")
    }
}

fun main(args: Array<String>) {
    //實例化一個類
    val Lili = BeijingPeople(true)
    Lili.avgSalary = 6000
    println(Lili.region)
    println(Lili.avgSalary)
    Lili.cook()

    println()
    val Jack = ShanghaiPeople(true)
    Lili.avgSalary = 5000
    println(Jack.region)
    println(Jack.avgSalary)
    Jack.cook()
}

Beijing
5100
我會宮爆雞丁,番茄炒雞蛋,蔥爆牛肉
我還會北京烤鴨,北京炸醬面,北京包子

Shanghai
4000
我會宮爆雞丁,番茄炒雞蛋,蔥爆牛肉
我還會大閘蟹,小湯包,芝士焗面

2.抽象類和接口

抽象類:理解為半成品
接口:類似于協議或者某種成品就有的功能

接口
  • 不能有狀態
  • 必須有類對其進行實現后實現
  • 接口,直觀理解就是一種約定
   Kotlin的接口與Objective-C的Protocol比較類似
  • 舉例
interface InputDevice{
fun input(event: Any)
}
抽象類
  • 實現了一部分協議的半成品
  • 可以有狀態,可以有方法實現
  • 必須由子類繼承后使用
抽象類和接口的共性
  • 比較抽象,不能直接實例化
  • 有需要子類(實現類)實現的方法
  • 父類(接口)變量可以接受子類(實現類)的實例賦值
抽象類和接口的區別
  • 抽象類有狀態,接口沒有狀態
  • 抽象類有實現方法,接口只能有無狀態的默認實現
  • 抽象類只能單繼承,接口可以多實現
  • 抽象類反應本質,接口提現能力
interface InputDevice {
    fun input(event: Any)
}

interface USBInputDevice : InputDevice

interface BLEInputDevice : InputDevice

abstract  class USBMouse(val name:String) : USBInputDevice,OpticalMouse {
    override fun input(event: Any) {

    }

    override fun toString(): String {
        return name
    }
}
interface OpticalMouse{

}

class LogitechMouse:USBMouse("羅技鼠標"){

}

class Computer {

    fun addUSBInputDevice(inputDevice: USBInputDevice) {
        //插入輸入設備
        println("add usb input device:$inputDevice")
    }

    fun addBLEInputDevice(inputDevice: BLEInputDevice) {
        //插入輸入設備
        println("add input device:$inputDevice")
    }

    fun addInputDevice(inputDevice: InputDevice) {
        when (inputDevice) {
            is BLEInputDevice -> {
                addBLEInputDevice(inputDevice)
            }
            is USBInputDevice -> {
                addUSBInputDevice(inputDevice)
            }
            else -> {
                throw IllegalArgumentException("輸入設備不支持")
            }
        }
    }
}

fun main(args: Array<String>) {
    val computer = Computer()
    val mouse = LogitechMouse()
    computer.addInputDevice(mouse)
}
add usb input device:羅技鼠標

3.繼承(實現)

繼承(實現)語法要點
  • 父類需要open才可以被繼承
  • 父類方法、需要open才可以被覆寫
  • 接口、接口方法、抽象類默認為open
  • 覆寫父類(接口)成員需要override關鍵字
  • class D:A(),B,C
  • 注意繼承類時實際上調用了父類的構造方法
  • 類只能單繼承,接口可以多實現
接口代理
  • class Manager(driver:Driver):Driver by driver
  • 接口方法實現交給代理類實現
接口方法沖突
  • 接口方法可以有默認實現
  • 簽名一致且返回值有沖突
  • 子類(實現類)必須覆寫沖突方法
  • super<[父類(接口)名]>.方法名
abstract class Person(open val age: Int) {
    open fun work() {

    }
}

class Coder(age: Int) : Person(age) {
    override val age: Int
        get() = 0

    override fun work() {
        super.work()
        println("我是程序員")
    }
}

class Doctor(override val age: Int) : Person(age) {
    override fun work() {
        super.work()
        println("我是醫生")
    }
}

fun main(args: Array<String>) {
    val person = Coder(28)
    person.work()
    println(person.age)
    val person2 = Doctor(25)
    person2.work()
    println(person2.age)
}

我是程序員
0
我是醫生
25
class Manager : Driver, Writer {
    override fun write() {

    }

    override fun drive() {

    }
}

//寫法一
//class SeniorManager(val driver: Driver,val writer: Writer):Driver,Writer{
//    override fun drive(){
//        driver.drive()
//    }
//    override fun write(){
//        writer.write()
//    }
//}
//簡寫二
class SeniorManager(val driver: Driver, val writer: Writer) : Driver by driver, Writer by writer

class CarDriver : Driver {
    override fun drive() {
        println("開車了")
    }
}
class PPTWriter :Writer{
    override fun write(){
        println("做PPT了")
    }
}

interface Driver {
    fun drive() {

    }
}

interface Writer {
    fun write() {
    }
}

fun main(args: Array<String>) {
    val driver = CarDriver()
    val writer = PPTWriter()
    val seniorManager = SeniorManager(driver,writer)
    seniorManager.drive()
    seniorManager.write()
}
開車了
做PPT了
interface InputDevice {
    fun input(event: Any)
}

interface USBInputDevice : InputDevice

interface BLEInputDevice : InputDevice

abstract  class USBMouse(val name:String) : USBInputDevice,OpticalMouse {
    override fun input(event: Any) {

    }

    override fun toString(): String {
        return name
    }
}
interface OpticalMouse{

}

class LogitechMouse:USBMouse("羅技鼠標"){

}

class Computer {

    fun addUSBInputDevice(inputDevice: USBInputDevice) {
        //插入輸入設備
        println("add usb input device:$inputDevice")
    }

    fun addBLEInputDevice(inputDevice: BLEInputDevice) {
        //插入輸入設備
        println("add input device:$inputDevice")
    }

    fun addInputDevice(inputDevice: InputDevice) {
        when (inputDevice) {
            is BLEInputDevice -> {
                addBLEInputDevice(inputDevice)
            }
            is USBInputDevice -> {
                addUSBInputDevice(inputDevice)
            }
            else -> {
                throw IllegalArgumentException("輸入設備不支持")
            }
        }
    }
}

fun main(args: Array<String>) {
    val computer = Computer()
    val mouse = LogitechMouse()
    computer.addInputDevice(mouse)
}
add usb input device:羅技鼠標

4.Kotlin和Java可見性對比

Kotlin Java
public public
private priavte
protected protected
- default(包內可見)
internal(模塊類可見) -

5.對象聲明和表達式

object
  • 只有一個實例的類
  • 不能自定義構造方法
  • 可以實現接口、繼承父類
  • 本質上就是單例模式最基本的實現
/**
 * 有時候只要對某各類進行改造,供零時使用,避免繼承
 * 對象聲明和表達式就很有作用
 *
 * 面向對象編程的優化,避免一些繼承導致的代價過高,保持代碼的整潔
 */


//對中國人來說,這個類,可能各省人適合繼承
open class Chinese(var name: String) {

    open val skin = "yellow"
}

fun main(args: Array<String>) {
    //但如果外國人入籍,就不適合用繼承
    //對象表達式:val 對象名 = object : 類,接口 {//屬性或方法的override定義}

    val Jack= object : Chinese ("Jack Marry")
    {
        override val skin = "white"
    }

    println(Jack.skin)

    //純對象表達式:臨時使用,無須繼承任何類
    val loc = object {
        var x = 100
        var y = 200
    }
    println(loc.x)
    //相當于調用函數
    NetworkRequestManager.register()

    //半生對象的方法,與類關聯性強
    IDCard.create()
}

//對象聲明,不能用在函數中
    //一般用于對其他類使用上的包裝
object NetworkRequestManager{
    fun register(){
        println("連接網絡注冊中...")
    }
}

//半生對象:一般用于創建一個類的實例"工廠"方法
//Java中的 靜態成員
class IDCard{
    companion object {
        fun create() = IDCard()
    }
}

white
100
連接網絡注冊中...

6.半生對象與靜態成員

  • 每個類可以對應一個伴生對象
  • 伴生對象的成員全局獨一份
  • 伴生對象的成員類似Java的靜態成員
  • 靜態成員考慮用包級函數、變量替代
  • JvmField和JvmStatic的使用
fun main(args: Array<String>) {
    val a = minOf(args[0].toInt(), args[1].toInt())

    val latitude = Latitude.ofDouble(3.0)

    val latitude2 = Latitude.ofLatitude(latitude)

    println(Latitude.TAG)
}

class Latitude private constructor(val value: Double) {
    companion object {
        @JvmStatic
        fun ofDouble(double: Double): Latitude {
            return Latitude(double)
        }

        fun ofLatitude(latitude: Latitude): Latitude {
            return Latitude(latitude.value)
        }
        @JvmField
        val TAG:String = "Latitude"
    }
}
Latitude

JvmStatic JvmField 用于Java類調用

public class StaticJava {
    public static void main(String[] args){
        Latitude latitude = Latitude.ofDouble(4);
        System.out.println("Java:"+Latitude.TAG);
    }
}
Java:Latitude

6.方法重載Overloads 和默認參數

方法重載Overloads
  • 名稱相同、參數不同的方法
  • Jvm函數簽名的概念:函數名、參數列表
  • 跟返回值沒有關系
默認參數
  • 為函數參數設定一個默認值
  • 可以為任意位置的參數設置默認值
  • 函數調用產生混淆時用具名參數
兩者關系
  • 避免定義關系不大的重載
  • 方法重載最好能轉化為默認參數
  • 不好的設計:
- List.remove(int)
- List.remove(Object)
//方法重載和函數名有和參數列表有關系,和函數返回值沒關系
class Overloads {
    //    fun a(): Int {
//        return 0
//    }
    @JvmOverloads //默認參數 是給java調用的
    fun a(int: Int = 0): Int {
        return int
    }

}

fun main(args: Array<String>) {
    val overloads = Overloads()
    overloads.a()

    val integerList = ArrayList<Int>()
    integerList.add(555)
    integerList.add(2)
    integerList.add(3)
    integerList.add(4)
    integerList.add(9)
    integerList.add(45)

    println(integerList.toString())
    integerList.removeAt(1)
    integerList.remove(9)
    println(integerList.toString())
}
[555, 2, 3, 4, 9, 45]
[555, 3, 4, 45]

7.擴展成員

  • Java調用擴展成員類似調用靜態方法
  • 為現有類添加方法、屬性
- fun X.y():Z{...}
- val X.m 主義擴展屬性不能初始化,類似接口屬性
fun main(args: Array<String>) {

    println("abc" * 16)
    "abc".b = 5
    println("abc".b)
}

operator fun String.times(int: Int): String {

    val stringBuilder = StringBuilder()

    for (i in 0 until int) {
        stringBuilder.append(this)
    }
    return stringBuilder.toString()
}

val String.a: String
    get() = "abc"

var String.b: Int
    set(value) {
    }
    get() = 5
abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc
5

8.屬性代理

  • 定義方法:
- val/var <property name>:<Type> by
  <expression>
  • 代理者需要實現相應的setValue/getValue方法:
  • lazy原理剖析

9.數據類

  • 再見,JavaBean
  • 默認實現的copy,toString等方法
  • componentN方法
  • allOpen和noArg插件 在編譯器 把final關鍵字去掉
import com.haocai.kotlindemo.annotations.Poko

@Poko
data class Country(val id: Int, val name: String)

class ComponentX {
    operator fun component1(): String {
        return "您還,我是"
    }

    operator fun component2(): Int {
        return 1
    }

    operator fun component3(): Int {
        return 1
    }

    operator fun component4(): Int {
        return 0
    }
}

fun main(args: Array<String>) {
    val china = Country(0, "中國")
    println(china)
    println(china.component1())
    println(china.component2())

    val (id, name) = china
    println(id)
    println(name)

//    for ((index, value) in args.withIndex()) {
//        println(index)
//        println(value)
//    }

    val componentX = ComponentX()
    val (a, b, c, d) = componentX
    println("$a $b $c $d")
}

10.內部類

  • 定義在類內部的類
  • 與類成員有相似的訪問控制
  • 默認是靜態內部類,非靜態用inner關鍵字
  • this@Outter,this@Inner的用法
  • 沒有定義名字的內部類
  • 類名編譯時生成,類似Outter$1.class
  • 可繼承父類、實現多個接口,與Java注意區別
Kotlin寫法:
open class Outter {
    val a: Int = 0

    inner class Inner {
        val a: Int = 5
        fun hello() {
            println(this@Outter.a)
        }
    }
}

interface OnClickListener {
    fun onClick()
}

class View {
    var onClickListener: OnClickListener? = null
}

fun main(args: Array<String>) {
    val inner = Outter().Inner()
    inner.hello()

    val view = View()
    //匿名內部類 即可以實現接口,同時繼承外部類(如,Outter())
    view.onClickListener = object : Outter(),OnClickListener{
        override fun onClick() {

        }
    }
}
0
Java類似寫法:
public class InnerClassJava {
    private int a;

    public static void main(String... args) {
        InnerClassJava innerClassJava = new InnerClassJava();
        Inner inner = innerClassJava.new Inner();
        //Inner inner = new Inner(); 報錯 除非Inner 是靜態
        inner.hello();

        View view = new View();
        view.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick() {

            }
        });
    }

    public class Inner {
        public void hello() {
            System.out.println(InnerClassJava.this.a); //非靜態內部類可以持有外部類的狀態
        }
    }
}

0

11.枚舉

  • 實例可數的參數,注意枚舉也是類
  • 可以修改構造,添加成員
  • 可以提升代碼的表現力,也有一定的性能開銷
enum class LogLevel(val id: Int) {
    VERBOSE(0), DEBUG(1), INFO(2), WARN(3), ERROR(4), ASSERT(5);

    fun getTag(): String {
        return "$id,$name"
    }

    override fun toString(): String {
        return "$name,$ordinal"
    }
}

class LogLevel2 protected constructor() {
    companion object {
        val VERBOSE = LogLevel2()
        val DEBUG = LogLevel2
        val INFO = LogLevel2
        val WARN = LogLevel2
        val ERROR = LogLevel2
        val ASSERT = LogLevel2

    }
}

fun main(args: Array<String>) {
    println(LogLevel.INFO.getTag())
    println(LogLevel.DEBUG.ordinal)
    LogLevel.values().map(::println)
}
2,INFO
1
VERBOSE,0
DEBUG,1
INFO,2
WARN,3
ERROR,4
ASSERT,5

12.密封類

  • 子類可數(從v1.1開始,只需要與密封類在同一個文件中)
  • 枚舉是實例可數
sealed class PlayerCmd{
    class Play(val url:String ,val position:Long = 0):PlayerCmd()

    class Seek(val position:Long ):PlayerCmd()

    object Pause:PlayerCmd()

    object Resume:PlayerCmd()

    object Stop:PlayerCmd()
}

enum class PlayerState{
    IDLE,PAUSE,PLAYING
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,460評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,067評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,467評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,468評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,184評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,582評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,616評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,794評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,343評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,096評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,291評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,863評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,513評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,941評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,190評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,026評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,253評論 2 375

推薦閱讀更多精彩內容