第二章 K o t lin面向对象

一、抽象类与接口(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);

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容