Kotlin入门之面向对象

作者: wxyass 分类: Android,Kotlin 发布时间: 2018-03-14 12:54

一 面向对象-抽象类与接口(abstract,interface)

接口: 一个协议
抽象类: 一个半成品

二 子承父业-继承

2.1 继承(实现)语法要点

  • 类的继承/实现语法,使用冒号 class D : A(),B,C
  • 抽象类用冒号(:)继承 抽象方法用override复写
  • 注意继承类时,实际调用了父类构造方法
  • 类只能单继承,接口可以多实现
  • 父类需要 open 才可以被继承
  • 父类方法及属性需要open才可以被覆写
  • 接口,接口方法,抽象类默认为open
  • 覆写父类成员/接口成员需要用到override关键字
// 定义一个抽象类 构造方法参数用open修饰表示可重写
abstract class Person(open val age: Int){
    // 定义一个抽象方法
    abstract fun work()
}

// 子类继承父类
class Doctor(age: Int): Person(age){
    // 如果继承的是抽象类,必须重写所有的抽象方法
    override fun work() {
        println("我是医生,我在给病人看病")
    }
}

// 子类继承父类
class MaNong(age: Int): Person(age){

    // 重写参数值
    override val age: Int
        get() = 0

    override fun work() {
        println("我是码农,我在写代码")
    }
}

fun main(args: Array<String>) {
    val person: Person = MaNong(23)
    person.work()
    println(person.age)

    val person2 : Person = Doctor(24)
    person2.work()
    println(person2.age)
}



我是码农,我在写代码
0
我是医生,我在给病人看病
24

2.2 接口代理

1 接口代理语法 class Manager(driver:Driver):Driver by driver

2 通过接口代理,让接口方法的实现交给代理类实现

// 普通经理, 实现接口(用冒号)
class Manager: Driver, Writer {
    override fun write() {

    }

    override fun drive() {

    }
}

// 资深经理  使用接口代理
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()
}

2.3 接口方法冲突

1 接口方法可以有默认实现    

2 签名一致且返回值相同的冲突  

3 子类(实现类)必须覆写冲突方法   

4 super<父类(接口)名>.[方法名]([参数列表])    

当接口的签名一样(方法名一样,参数列表一样),返回值类型也一样的处理方法,使用super<类名>.方法名

// 定义一个抽象父类
abstract class A{
    open fun x(): Int = 5
}

// 定义接口B
interface B{
    fun x(): Int = 1
}

// 定义皆苦C
interface C{
    fun x(): Int = 0
}

// D 继承类,实现接口
class D(var y: Int = 0): A(), B, C{

    override fun x(): Int {
        println("call x(): Int in D")
        if(y > 0){
            return y
        }else if(y < -200){
            return super<C>.x()
        }else if(y < -100){
            return super<B>.x()
        }else{
            return super<A>.x()
        }
    }
}

fun main(args: Array<String>) {
    println(D(3).x())
    println(D(-10).x())
    println(D(-110).x())
    println(D(-10000).x())
}


call x(): Int in D
3
call x(): Int in D
5
call x(): Int in D
1
call x(): Int in D
0  

三 类及其成员变量的可见性(private protected internal public)

可见行对比

class House

class Flower

class Countyard{
    // private 类私有,只能在这个类中调用,在其他地方不可调用
    private val house: House = House()
    private val flower: Flower = Flower()
}

class ForbiddenCity{
    // internal 模块内可用,只在当前Module中可用
    internal val houses = arrayOf(House(), House())
    // 默认public  任何地方都可调用  
    val flowers = arrayOf(Flower(), Flower())
}

fun main(args: Array<String>) {
    val countyard = Countyard()
    val fc = ForbiddenCity()
    println(fc.flowers)
}

四 object

1 只是一个实例的类

2 不能自定义构造方法

3 可以实现接口,继承父类

4 本质上就是单例模式最基本的实现

class Driver

// 定义接口 是否有外接硬盘插入
interface OnExternalDriverMountListener{
    fun onMount(driver: Driver)

    fun onUnmount(driver: Driver)
}

// 定义抽象父类
abstract class Player

// 音乐播放器 继承父类,实现接口
object MusicPlayer: Player(), OnExternalDriverMountListener{
    override fun onMount(driver: Driver) {

    }

    override fun onUnmount(driver: Driver) {

    }

    // 成员变量
    val state : Int = 0

    // 成员方法
    fun play(url : String){

    }

    // 成员方法
    fun stop(){

    }
}

五 伴生对象与静态成员

1 每个类可以对应一个伴生对象 使用companion object关键字

2 伴生对象的成员全局独一份

3 伴生对象的成员类似Java的静态成员

4 静态成员考虑用包级函数,包级变量替代

5 JvmField和JVMStatic的使用,这样可以在Java中调用

fun main(args: Array<String>) {
    val latitude = Latitude.ofDouble(3.0)
    val latitude2 = Latitude.ofLatitude(latitude)

    println(Latitude.TAG)
}

// 纬度
class Latitude private constructor(val value: Double){
    // 这个类的伴生对象
    companion object{
        @JvmStatic // @JvmStatic 便于在java中调用
        fun ofDouble(double: Double): Latitude{
            return Latitude(double)
        }

        // 相当于java中静态方法
        fun ofLatitude(latitude: Latitude): Latitude{
            return Latitude(latitude.value)
        }

        @JvmField // @JvmField 便于在java中调用
        val TAG: String = "Latitude"
    }
}

六 方法重载与默认参数

1 overloads 方法重载

2 名称相同,参数不同的方法

3 Jvm函数签名的概念:函数名,参数列表

4 方法重载与方法签名(方法名,参数列表)有关系,跟返回值类型没有关系

5 默认参数->为函数参数设定一个默认值

6 可以在任意位置的参数设置默认值

7 函数调用产生混淆时用具名参数

8 使用@JvmOverloads后,可在java中调用无参函数

9 注意要尽量避免定义关系不大的重载

class Overloads{
    @JvmOverloads // @JvmOverloads 可在java中调用不传参数的a()  
    fun a(int: Int = 0): Int{
        return int
    }
}

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

    val integerList = ArrayList<Int>()
    integerList.add(13)
    integerList.add(2)
    integerList.add(3)
    integerList.add(23)
    integerList.add(5)
    integerList.add(15)
    integerList.add(50)
    integerList.add(500)
    println(integerList)

    integerList.removeAt(1)
    integerList.remove(5)
    println(integerList)
}

java中调用

public class Bug {
    public static void main(String... args) {
        Overloads overloads = new Overloads();
        // 如果在Kotlin中没有@JvmOverloads,在这里调用会报错
        overloads.a();
    }
}

七 扩展成员

1 为现有的类添加方法,属性

2 写法

fun X.y():Z{...}  
val X.m  //注意扩展属性不能初始化,类似接口属性  

3 Java调用扩展成员类似调用静态方法

举例

fun main(args: Array<String>) {
    println("abc" * 16)
    "abc".b = 5
    println("abc".b)

    println("*" * 16)
}

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
****************  

java中调用

public class ExtendsJava {
    public static void main(String... args) {
        System.out.println(ExtendsKt.times("abc", 16));
    }
}


abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc  

八 属性代理

定义方法

val/var <property name>:<Type> by <expression>  

代理者需要实现相应的 setValue/getValue 方法

lazy原理剖析

class Delegates{
    val hello by lazy {
        "HelloWorld"
    }

    val hello2 by X()

    var hello3 by X()
}

class X{
    private var value: String? = null

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("getValue: $thisRef -> ${property.name}")
        return value?: ""
    }

    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 = Delegates()
    println(delegates.hello)
    println(delegates.hello2)
    println(delegates.hello3)
    delegates.hello3 = "value of hello3"
    println(delegates.hello3)
}

九 数据类data class(allOpen noArg插件 再见 JavaBean)

参考 Kotlin编译器插件

默认实现的copy,toString等方法

componentN 方法

allOpen 和 noArg 插件

使用data class需要安装2个官方插件
1 在build中配置插件

plugins {
    id 'java'
    id 'org.jetbrains.kotlin.jvm' version '1.2.51'
    id "org.jetbrains.kotlin.plugin.allopen" version "1.2.51"
    id "org.jetbrains.kotlin.plugin.noarg" version "1.2.51"
}

apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'kotlin-noarg'
apply plugin: 'kotlin-allopen'

noArg{
    annotation("com.wxyass.kotlin.annotations.PoKo")
}

allOpen{
    annotation("com.wxyass.kotlin.annotations.PoKo")
}

2 在项目中新建PoKo.kt文件

annotation class PoKo  

3 在data class添加注解@PoKo

/**
 * 数据类
 * Created by wxyass on 2/6/18.
 */
@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)

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


Country(id=0, name=中国)
0
中国
0
中国
您好,我是 110

十 内部类(this@Outter this@Inner)

定义在类内部的类

与类成员有相似的访问控制

默认是静态内部类,非静态用inner关键字

this@Outter,this@Inner的用法

匿名内部类: 没有定义名字的内部类

匿名内部类的类名编译时生成,类似Outter$1.class   

匿名内部类可继承父类,可实现多个接口,与Java注意区别

/**
 * 内部类
 * Created by benny on 4/4/17.
 */
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()

    val view = View()
    view.onClickListener = object : Outter(), OnClickListener{
        override fun onClick() {

        }
    }
}

java中的内部类

public class InnerClassJava {
    private int a;

    public class Inner{
        public void hello(){
            System.out.println(InnerClassJava.this.a);
        }
    }

    public static void main(String... args) {
        InnerClassJava innerClassJava = new InnerClassJava();
        View view = new View();
        view.setOnClickListener(new OnClickListener() {
            public void onClick() {

            }
        });
    }
}   

十一 枚举(enum)

实例可数的类,注意枚举也是类

可以修改构造,添加成员

可以提升代码的表现力,也有一定的性能开销

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"
    }
}

fun main(args: Array<String>) {
    println(LogLevel.DEBUG.ordinal)
    LogLevel.values().map(::println)

    println(LogLevel.valueOf("ERROR"))
}

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


1
VERBOSE, 0
DEBUG, 1
INFO, 2
WARN, 3
ERROR, 4
ASSERT, 5
ERROR, 4

十二 密封类(seaied Class)

子类可数

<v1.1,子类必须定义为密封类的内部类  
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
}

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

邮箱地址不会被公开。 必填项已用*标注