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
}

推荐阅读更多精彩内容