Kotlin——面向对象(4)

2018-09-05  本文已影响14人  被时光移动的城

1、面向对象-抽象类与接口

1)什么是接口
接口,直观理解就是一种约定
kotlin的接口与Objcet-C的Protocal比较类似
举例:

interface InputDevice{
fun input(event:Any)
}

2}接口
a.接口不能有状态
b.必须由类对其进行实现后使用
3)抽象类
a.实现了一部分协议的半成品
b.可以有状态,可以有方法实现
c.必须由子类继承后使用
4)抽象类和接口的共性
a.比较抽象,不能直接实例化
b.有需要子类(实现类)实现的方法
c.父类(接口)变量可以接受子类(实现类)的实例赋值
d.抽象类反映本质,接口体现能力
5)抽象类和接口的区别
a.抽象类有状态,接口没有状态
b.抽象类有方法实现,接口只能有无状态的默认实现
c.抽象类只能单继承,接口可以多实现

2、继承

1)父类需要open才可以被继承
2)父类方法、属性需要open才可以被复写
3)接口、接口方法、抽象类默认为open
4)复写父类(实现接口)的方法需要override关键字
5)class D:A(),B,C
6)继承类时实际上是调用了父类构造方法
7)类只能单继承,接口可以多实现
接口代理

class Leader(val dr:Drive,val pp:PPT):D by dr,P by pp

接口方法实现交给代理类实现
事例:

interface  D{
    fun drive()
}

interface P{
    fun write()
}
 class Drive:D{
   override fun drive(){
        println("开车")
    }
}

class PPT:P{
  override fun write(){
        println("写ppt...")
    }
}

//接口代理
class Leader(val dr:Drive,val pp:PPT):D by dr,P by pp

fun main(args: Array<String>) {
    val lead = Leader(pp=PPT(),dr=Drive())
    //接口代理调用
    lead.drive()
    lead.write()
}

接口方法冲突
1)接口方法可以有默认实现
2)方法名、方法参数以及返回值相同,否则会有冲突
3)子类(实现类)必须复写冲突方法
4)super<[父类(接口)名]>.[方法名]([参数列表])
事例:

open class A {
    open fun x(): Int = 'A'.toInt()
}

interface B {
    fun x(): Int = 'B'.toInt()
}

interface C {
    fun x(): Int = 'C'.toInt()
}

class F(var y: Int) : B, C, A() {
    override fun x(): Int {
        return when {
            y > 0 -> super<A>.x()
            y < 0 -> super<B>.x()
            else -> super<C>.x()
        }
    }
}

fun main(args: Array<String>) {
    println(F(-1).x())//B
    println(F(0).x())//C
    println(F(1).x())//A
}

运行结果:66,67,65

3、类及其成员的可见性(private,protected,internal,public)

注:internal在和java兼容方面依旧存在问题,暂不建议使用


可见性对比.png

4、object

1)只有一个实例的类
2)不能自定义构造方法
3)可以实现接口、继承父类
4)本质上就是单例模式最基本的实现
注:如果不考虑懒加载等因素,Kotlin中写单例,使用的是object。

5、伴生对象与静态成员

1)每个类可以对应一个伴生对象
2)伴生对象的成员全局独一份
3)伴生对象的成员类似Java中的静态成员
4)静态成员考虑用包级函数、变量替代
5)JvmField和JvmStatic的使用
事例:
无论加不加@JvmStatic@JvmField在kotlin都可正常调用,而Java若想调用,则必须添加这两个注解。

class Latitude private constructor(val d:Double){
    companion object {
        @JvmStatic
        fun la(d: Double):Latitude{
            return Latitude(d)
        }
        @JvmStatic
        fun getD(latitude: Latitude):Latitude{
            return Latitude(latitude.d);
        }
        @JvmField
        val TAG = "TAG"
    }
}

kotlin中调用

fun main(args: Array<String>) {
var latitude = Latitude.la(6.66)
val latitude2 = Latitude.getD(latitude)
val tag = Latitude.TAG
}

java中调用

Latitude latitude = Latitude.la(3.234);
Latitude latitude2 = Latitude.getD(latitude);
String tag = Latitude.TAG;

6、方法重载与默认参数

1)方法重载
a.Overloads
b.名称相同、参数不同的方法
c.Jvm函数签名的概念:函数名、参数列表
d.跟返回值无关
2)默认参数
a.为函数参数设定一个默认值
b.可以为任意位置的参数设置默认值
c.函数调用产生混淆时用具名参数
3)方法重载和默认参数
a.二者的相关性以及@JvmOverloads(给java中调用kotin中的默认参数时使用)
事例:

class Overloads {
//    fun a(): Int {
//        return 0
//    }
   //  @JvmOverloads
    fun a(int: Int = 0): Int {
        return int
    }

    fun a(str: String): Int {
        return str.toInt()
    }
}

fun main(args: Array<String>) {
    val ovarLoads = Overloads()
    ovarLoads.a(1)
    ovarLoads.a("2")
//kotlin中调用
    println(ovarLoads.a())
}
未加@JvmOverLoads时javad调用.png
加上@JvmOverLoads时java的调用.png

b.避免定义关系不大的重载
c.不好的设计

List.remove(Int)
List.remove(Object)

事例:


java中remove.png
kotlin中remove.png

7、扩展成员

为现有类添加方法属性

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

二元运算符.png
fun main(args: Array<String>) {
    println("abc".times(6))
    println("a" * 6)
    println("a".a )
    println("b".b)
}

operator fun String.times(num: Int): String {
    val sb = StringBuffer()
    if (num > 0) {
        for (i in 0 until num) {
            sb.append(this)
        }
    }
    return sb.toString()
}

val String.a
    get() = 5
var String.b
    set(value){}
    get() = 6

运行结果:


运行结果.png

8、属性代理

1)定义方法

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

2)代理者需要实现相应的setValue/getValue方法
事例:

class Delegate {
    val x1 by lazy {
        "Hello X"
    }
    var x2 by X()
    var x3 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 delegate = Delegate()
    println(delegate.x1)
    println(delegate.x2)
    println(delegate.x3)
    delegate.x3 = "666666666666666666666"
    println(delegate.x3)
}

输出结果:


运行结果截图.png

9、数据类(allOpen,noArg插件,再见,javaBean)

1)data class默认实现了copy、toString、hashCode、equals等方法
2)componentN方法
3)allOpen和noArg插件

10、内部类(this@Outter,this@Inner)

内部类
a.定义在类内部的类
b.与类成员有相似的访问控制
c.默认是静态内部类,非静态用inner关键字
d.this@Outter,this@Inner的用法
匿名内部类
a.没有定义名字的内部类
b.类名编译时生成,类似Outter$1.class这样的类名
c.可继承父类、实现多个接口,与Java注意区别

11、枚举(enum)

实例可数的类,注意枚举也是类
可以修改构造,添加成员
可以提升代码的表现力,也有一定的性能开销

12、密封类(sealed Class)

1)子类可数
kotlin版本<1.1,子类必须定义为封闭类的内部类
v1.1,子类只需要与密封类在同一文件中
2)与枚举类似却又不同

上一篇下一篇

猜你喜欢

热点阅读