第二章 K o t lin面向对象

作者: Serenity那年 | 来源:发表于2017-07-17 18:34 被阅读74次
    一、抽象类与接口(abstract,interface)
    • 1.抽象类
    • 1.可以简单的理解为:实现一部分协议的半成品(类可以看成成品),比类抽象点,比接口具体点;
    • 2.抽象类是有类的特征的;
    • 3.抽象类可以有属性,可以有方法,并且方法可以有方法体;
    • 4.要想重写抽象类中的具体方法,需要将此方法用open修饰符修饰,才能重写;
    • 5.必须由子类继承后使用;
    • 6.抽象类默认就是open的,里面的抽象方法默认也是open的;具体方法如果想要被子类重写必须要用open修饰;
    abstract class C {
        //抽象类可以有状态--成员变量和具体方法
        var i = 0 //这个值可以变
    
        open fun test() {
    
        }
    
        //抽象方法不能有函数体,必须由子类实现
        abstract fun abstractMethod()
    }
    
    /**
     * 如果继承的是抽象类或者类,必须写上父类的构造方法
     */
    class D: C() {
        override fun abstractMethod() {
    
        }
    
        //要想复写抽象类的具体方法,必须加上修饰符open,否则访问不到
        override fun test() {
            super.test()
        }
    
    }
    
    • 2.接口
    • 1.可以简单的理解为:协议,规定;
    • 2.在kotlin可以给接口方法一个默认实现,但是默认实现不能有状态,状态必须在实现类中定义;
    • 3.Kotlin的接口与Object-C的Protocol比较类似;
    • 4.接口不能有状态;
    • 5.必须由类对其进行实现后使用;
    • 反应事物的能力;
    interface TestInterface {
        //可以定义,但是不能初始化
        //这里相当于定义了一个setI()和getI()的接口方法,不是真正定义了一个状态变量,
        //因为不能写get方法
        val i :Int
        //接口只能有无状态的默认实现
        fun interfaceMethod(){
            println(i)
        }
    
    }
    
    class ImplTest : TestInterface {
        //必须实现成员变量,否则编译报错
        override val i: Int
            get() = 0 
    
    
        //由于TestInterface中的方法有个默认实现,在这里可以不用实现此方法
    //    override fun interfaceMethod() {
    //        super.interfaceMethod()
    //    }
    }
    
    //成员变量的实现还可以用构造器的方式实现
    interface TestInterface {
        val i :Int
    
        fun interfaceMethod(){
            println(i)
        }
    
    }
    
    class ImplTest(override val i: Int) : TestInterface {
        //必须实现成员变量,否则编译报错
    //    override val i: Int
    //        get() = 0
    
    
        //由于TestInterface中的方法有个默认实现,在这里可以不用实现此方法
    //    override fun interfaceMethod() {
    //        super.interfaceMethod()
    //    }
    }
    

    总结:
    1.抽象类具有类的特征,可以有内部的状态(也就是可以有成员变量和具体方法);接口可以有不能初始化的成员变量,这个成员变量其实就是一个接口方法;也就是说抽象类有状态,接口没有状态;抽象类有方法实现,接口只能有无状态的默认实现;
    2.单继承多实现;
    3.抽象类反映事物的本质,不管男人、女人,本质都是人;
    4.接口反映事物的能力,能力可以有多个,但是你还是你--说的是抽象类;
    5.如果要用抽象类和接口描述一个东西,那这个东西的中心词前面的修饰词就是接口要负责的部分,中心词是抽象类负责的部分;

    举个例子:Mac 笔记本 电脑
    那么中心词是“电脑”--电脑用抽象类定义;
    Mac和笔记本是修饰词--用接口定义;

    接口与抽象类的共性:
    1.比较抽象,不能直接实例化;
    2.需要由子类或者实现类实现他们的抽象方法;
    3.父类(接口)变量可以接受子类(实现类)的实例赋值;也就是父类的引用指向子类;

    备注:如果不是抽象类,想要被继承的话,必须加上open关键字,否则无法被继承,默认为final;类中的方法想要被覆写,也要先open;abstrac类、abstract方法和接口、接口方法不需要写open,默认就是open

    /**
     * 如果想要被继承,必须用open修饰
     * 属性想要被覆写,也必须open修饰
     */
    open class Person(open val age: Int,open var name:String){
    
        //如果方法想要被子类重写,必须用open修饰
        open fun work(){
    
        }
    
    }
    
    //覆写属性时,必须用voerride
    class MaNong(override val age: Int,name:String): Person(age,name) {
    
        //子类重写父类的方法,必须要用override修饰
        override fun work() {
            super.work()
        }
    
    }
    
    class Enginer(age: Int,name:String) : Person(age,name){
    
        //覆写属性的第二种方式
        //val修饰的属性只有get方法,因为不可变,不能有set方法
        override val age: Int
            get() = 0//重新赋值
    
        override var name: String = ""
            get() = super.name
            set(value) {
                field = value
            }
    }
    
    • 通过by关键字实现接口代理
    interface Driver{
        fun drive()
    }
    
    interface Writer{
        fun write()
    }
    
    class ImpDriver :Driver {
        override fun drive() {
            println("驾驶")
        }
    }
    
    class ImpWriter:Writer {
        override fun write() {
            println("写作")
        }
    }
    
    /**
     * 通过by关键字实现接口代理
     */
    class ImplPreson(val driver: Driver,val writer: Writer):Driver by driver,Writer by writer{
        //通过使用by关键字进行接口代理,下面的方法不用实现
    //    override fun drive() {
    //        driver.drive()
    //    }
    //
    //    override fun write() {
    //        writer.write()
    //    }
    }
    
    fun main(args: Array<String>) {
        var driver = ImpDriver()
        var writer = ImpWriter()
        var person = ImplPreson(driver,writer)
        person.drive()
        person.write()
    }
    
    二、类的鼻祖-object-单例

    1.在实际的使用中只有一个对象--也就是kotlin中的单例;相当于java中的最简单的单例实现;
    2.使用时可以和普通的类一样,可以有成员和方法,只是class的一种特殊情形;
    3.可以实现接口,继承类;

    interface Play{
        fun play()
    
    }
    
    abstract class Sleep {
        abstract fun sleep()
    }
    
    object Manager :Play,Sleep(){
        override fun play() {
    
        }
    
        override fun sleep() {
        }
    
        lateinit var name:String
        fun work() {
    
        }
    }
    
    fun main(args: Array<String>) {
        Manager.work()
        Manager.sleep()
        Manager.name = "android"
    }
    
    三、伴生对象与静态成员--companion object

    1.用来实现静态方法和静态属性;
    2.如果想和Java中静态方法和属性的调用一直的话,可以在静态属性上添加@ JvmField,在静态方法上添加@ JvmStatic
    实现方式如下:

    fun main(args: Array<String>) {
    
        var latitude = Latitude.ofDouble(3.0)
        var copy = Latitude.copy(latitude)
        println( Latitude.param )
    }
    
    class Latitude private constructor(val value: Double)
    {
        companion object {
    
            @JvmField
            //静态属性
            val param:Double = 100.0
    
            @JvmStatic
            //静态方法
            fun ofDouble(double: Double):Latitude {
                return Latitude(double)
            }
    
            fun copy(latitude: Latitude):Latitude{
                return Latitude(latitude.value)
            }
        }
    }
    

    总结:
    1.每个类都对应一个伴生对象,相当于类对应的静态成员;
    2.伴生对象的成员全局独一份,这个是针对于类来说的;
    3.伴生对象的成员相当于我们Java中的静态成员;
    3.静态成员尽量使用包级函数、包级变量代替;
    4.在Java中调用kotlin中的伴生对象时,kotlin中的伴生对象使用JvmField和JvmStatic注释可以保证使用时调用一致性;

    四、方法重载和默认参数--overload

    1.方法重载--方法名相同,参数不同;与返回值无关;
    JVM函数签名:只与函数名和参数列表相关;与返回值无关;

    class Overloads {
        fun a(): Int {
    
            return 0
        }
    
        fun a(param: Int ): Int {
            return param
        }
    
    }
    
    fun main(args: Array<String>) {
        var overload = Overloads()
        overload.a()
        overload.a(10)
    }
    

    2.方法重载可以转换为默认参数;
    可以为任意位置的参数设置默认参数;函数调用产生混淆时用具名函数

    class Overloads {
    //    fun a(): Int {
    //
    //        return 0
    //    }
        //这个注解是为了给Java中调用kotlin中的重载方法使用
        @JvmOverloads
        fun a(param: Int = 0): Int {
            return param
        }
    
    }
    
    fun main(args: Array<String>) {
        var overload = Overloads()
        overload.a()//不传任何参数,会使用默认参数0
        overload.a(10)
    }
    
    五、扩展成员

    1.为现有类添加方法、属性;

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

    2.Java调用扩展成员类似调用静态方法;

    下面展示扩展属性和扩展方法:具体的看下面的示例:

    fun main(args: Array<String>) {
        println("abc".copy(4))
        //输出值:abcabcabcabc
    
        println("abc".param)
        //输出值:扩展属性
    }
    
    /**
     * 扩展方法使用XXX.方法名
     * 那么在方法中this就代表XXX
     */
    fun String.copy(int: Int) :String{
        var stringBuilder = StringBuilder()
        for (i in 0 until int) {
            stringBuilder.append(this)
        }
        return stringBuilder.toString()
    }
    
    /**
     * 扩展属性只用val修饰,只能有get方法;用var修饰虽然有set方法,但是无法使用field
     * 它的意思是所有的字符串都有个param属性,其实就是调用了get方法
     */
    val String.param : String
     get() = "扩展属性"
    
    var Int.params:Int
     set(value) {}
     get() = 100
    
    六、属性代理

    定义方法:
    val/var <property name>:<Type> by <expression>
    Type类型可以不写,让编译器自行推导;
    expression代表的代理对象;
    代理者需要实现相应的setValue/getValue方法;
    代理的val只实现setValue方法,代理var的需要实现setValue和getValue方法;

    
    import kotlin.reflect.KProperty
    
    /**
     * Created by serenitynanian on 2017/7/14.
     * 属性代理
     */
    class Delegates{
    
        //一旦被lazy by了,就相当于只有在第一次访问到hello时才会初始化
        //lazy就是代理,对于val的变量的代理,必须实现getValue方法
        val hello by lazy {
            "Hello world"
        }
    
        //对hello1进行获取的时候,相当于hello1的值交给了X的getValue
        val hello1 by X()
    
        //对hello2设置值的时候,相当于调用了X的setValue;
        //对hello2进行获取的时候,相当于调用了X的getValue方法
        var hello2 by X()
    }
    
    class X{
        private var value:String? = null
        //下面的这个方法拷贝的lazy中的getValue方法,进行了一点改造
        /**
         * thisRef代表的是Delegates对象 property.name代表的是代理的变量名
         */
        operator fun getValue(thisRef: Any?, property: KProperty<*>): String{
            println("getValue------>$thisRef-------->$property")
            return value?:" no value"
        }
    
        //setValue方法最少需要三个参数
        operator fun setValue(thisRef: Any?, property: KProperty<*>,value:String){
            println("getValue------>$thisRef-------->$property------>$value")
            this.value = value
        }
    
    }
    
    fun main(args: Array<String>) {
        val delegates = Delegates()
    
        println(delegates.hello)
        println(delegates.hello1)
        println(delegates.hello2)
        delegates.hello2 = "set value ing..."
        println(delegates.hello2)
        /**
         *打印值:
         * Hello world
        getValue------>kotlindemo.Delegates@548c4f57-------->property hello1 (Kotlin reflection is not available)
        no value
        getValue------>kotlindemo.Delegates@548c4f57-------->property hello2 (Kotlin reflection is not available)
        no value
        getValue------>kotlindemo.Delegates@548c4f57-------->property hello2 (Kotlin reflection is not available)------>set value ing...
        getValue------>kotlindemo.Delegates@548c4f57-------->property hello2 (Kotlin reflection is not available)
        set value ing...
         */
    
    }
    
    七、数据类(allOpen,noArg插件)

    这个数据类类似Javabean,里面默认实现了get和set、toString方法,但是不同点是这个data class是被final修饰的,不能被继承,并且未提供无参的构造方法,所以不能完全替代javabean;为了能够实现javabean,官方提供了两个插件--allopen和noArg;
    具体配置如下:
    在项目下的build.gradle文件中配置如下:

    配置如图.png

    配置后需要自定义注解类:

    annotation class Poko
    
    import annotations.Poko
    
    /**
     * Created by serenitynanian on 2017/7/17.
     * 数据类(allOpen,noArg)类似java中的javabean
     * 但是不同点是:看字节码这个被data修饰的类是final的,不能被继承,并且未提供无参构造方法
     * Javabean可以被继承并且有无参构造方法;
     *
     */
    @Poko
    data class Country(var id:Int,var name:String)
    
    /**
     * 这个类说明了component并不是data class的专利,只是data class自动给它生成了component
     * 如果我们类中需要,我们就自己定义即可
     */
    class Componnent1{
        operator fun component1():String{
            return "component1"
        }
    
        operator fun component2():Int{
            return 100
        }
    
        operator fun component3():Int{
            return 22
        }
    }
    
    fun main(args: Array<String>) {
        var country = Country(0,"chain")
        println(country)
        println(country.component1())
        println(country.component2())
    
        var(country_id,country_name) = country
        println(country_id)
        println(country_name)
    
    //    for ((index, value) in args.withIndex()) {
    //        println("Index----->"+index)
    //        println("Value------>"+value)
    //    }
    
        var component = Componnent1()
        var (a,b,c) = component
        println("$a$b$c")
    
    }
    

    noArg给我们生成了无参的构造方法;
    allOpen给我们生成了不是final类型的类;

    不过这都是在编译器生成的,所以在代码中使用无参的是不能使用的,只能通过运行时反射得到,比如使用realm就可以使用这两个插件

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

    1.静态内部类不持有外部类的引用;
    2.非静态内部类持有外部类的引用或状态;
    3.kotlin中默认的内部类是静态的内部类;如果想使其为非静态的用inner关键字修饰
    4.非静态内部类访问外部类的属性,用this@Outter.属性名;类似java中的外部类名.this.属性名;
    5.如果内部类的实例需要依赖外部类的实例,就使用非静态内部类;如果内部类只是逻辑上与外部类有关联,那么实例的存在不依赖于外部类,就可以使用静态内部类;
    6.kotlin中的匿名内部类,可以继承一个类,实现多个接口:object:类名(),接口{方法体};编译时生成类名,类似Outter$1.class

    open class Outter{
    
        var a :Int = 0
    
        open fun OutterMethod() {
    
        }
    
        //默认静态的
        class StaticInner{
            fun hello() {
    
            }
        }
        //inner修饰后为非静态的
        inner class NoStaticInner{
    
            var a:Int = 5
    
            fun hello(){
                //为了保证引用到外部类的属性,用this.Outter.属性名
                println(this@Outter.a)
    
                println(this@NoStaticInner.a)//引用内部的a属性
                println(this.a)//引用内部的a属性
            }
        }
    }
    
    interface OnClickListener{
        fun onClick()
    }
    
    class View{
    
        var onClickListener:OnClickListener? = null
    }
    
    fun main(args: Array<String>) {
        //从这可以看出Inner默认的就是静态内部类
        //如果让Inner为非静态的,用inner修饰即可
        var staticInner = Outter.StaticInner()
    
        //用外部类的实例实例化
        var inner = Outter().NoStaticInner()
    
        var view = View()
        //kotlin中的匿名内部类可以继承一个类,实现多个接口
        view.onClickListener = object :Outter(),OnClickListener{
            override fun onClick() {
    
            }
    
            override fun OutterMethod() {
                super.OutterMethod()
            }
        }
    }
    
    九、枚举

    实例的个数确定,枚举也是类;
    可以修改构造,添加成员;
    可以提升代码的整洁,但是会损耗点性能;(比如说一些可以直接用int代表的,如果用枚举,会多消耗内存);

    /**
     * Created by serenitynanian on 2017/7/17.
     */
    enum class Season(var desc:String) {
    
        SPRING("春天"), SUMMER("夏天"), FALL("秋天"), WINTER("冬天");//如果下面有方法,这里必须写分好
    
        fun getTag() {
            //name是枚举自己的默认属性,比如第一个name为:SPRING
            println("$name------$desc")
        }
    }
    
    /**
     * 这个类就类似枚举类
     */
    class Test private constructor()
    {
        companion object {
            val SPRING = Test()
            val SUMMER = Test()
            val FALL = Test()
            val WINTER = Test()
        }
    }
    
    fun main(args: Array<String>) {
        println(Season.SPRING.getTag())
        println(Season.SPRING.ordinal)//此对象在整个枚举类中的次序
        println(Season.valueOf("SPRING"))//获得Spring对象
        Season.values().map(::print)//打印出所有对象
    }
    
    十、密封类(sealed classs)-子类有限的类,枚举是实例可数

    1.其子类只能定义在与sealed class同一文件中或者在seal class内部(kotlin版本小于1.1);

    相关文章

      网友评论

        本文标题:第二章 K o t lin面向对象

        本文链接:https://www.haomeiwen.com/subject/hputhxtx.html