新手上路,Kotlin学习笔记(三)---类、对象、接口

         入行没几年的小码农,近期学习Kotlin,做一份笔记记录,此文依据《Kotlin实战》这本书的流程记录,部分示例内容均摘自《Kotlin实战》,记下自己的理解,本篇记录Kotlin中对类,对象,接口的使用。



        Kotlin学习笔记系列

        新手上路,Kotlin学习笔记(一)-- Kotlin入门介绍

        新手上路,Kotlin学习笔记(二)---方法(函数)部分

        新手上路,Kotlin学习笔记(三)---类、对象、接口

        新手上路,Kotlin学习笔记(四)---Lambda表达式在Kotlin中的使用

        新手上路,Kotlin学习笔记(五)---Kotlin中的类型系统

        新手上路,Kotlin学习笔记(六)---运算符重载和其它约定

        新手上路,Kotlin学习笔记(七)---Lambda作为形参和返回值的使用

        新手上路,Kotlin学习笔记(八)---泛型的使用

        新手上路,Kotlin学习笔记(九)---注解和反射



一、接口和类的继承

        在我们的开发过程中,不可能没有接口的,那么,我们就先来看一下Kotlin中接口是如何使用的

        Kotlin中声明接口仍然使用interface关键字,示例如下

interface PrintListener {
    fun printByListener()
}

        实现接口也很简单,只需要在类的声明后面加上冒号“:”接口名称即可,实现方法的关键字是override,示例如下

class Student (var name : String) : PrintListener
{
    override fun printByListener() {
        println(this.toString())
    }
}

        在Kotlin中,实现接口和继承父类均以冒号表示,但是和Java一样,只能继承一个父类,可以实现多个接口。

        同时,Kotlin中的接口可以拥有一个默认的实现,示例如下

interface PrintListener {
    fun printByListener()
    fun printDefault() = println("default print")
}

        那么,如果有两个接口,都写了相同名称的默认方法,同时实现这两个接口的类,会怎么样呢?

interface PrintCallback {
    fun printDefault() = println("default printCallback")
}
class Student (var name : String) : PrintListener,PrintCallback
{
    override fun printByListener() {
        println(this.toString())
    }
}

     此时,编译器将提示错误:Class 'Student' must override public open fun printDefault(): Unit defined in com.mtf.kotlin.PrintListener because it inherits multiple interface methods of it

        我们必须给该类提供一个重名方法的实现

override fun printDefault() {
        super<PrintCallback>.printDefault()
        super<PrintListener>.printDefault()
        println("Stduent printDefault")
    }
        在调用接口的默认实现时,可以在super后使用<>区分是哪个接口的实现。


二、修饰符

        Java中如果不想让一个类被继承或者一个方法被子类重写,那么会使用final关键字进行修饰。在Kotlin中,final拥有同样的效果,不同的是,Kotlin中的类和方法,默认都是final的,如果我们想让这个类被继承,某个方法被重写,我们需要使用关键字open进行修饰。

open class Person(var age : Int = 0, var name : String = "", var addr : String = "") //open 修饰之后可以被子类继承
{
    open fun showName() = println(name) //open修饰之后的可以被子类重写
    fun showAddr() = println(addr) //默认为final的,不能被子类重写
}
class Student : Person(),PrintListener
{
    final override fun printByListener() = println(this.toString())//实现接口open的方法,默认是open的,如果不想被再次重写,需要显式使用final

    override fun showName() {
        print("Student")
        super.showName()
    }
}

        抽象类用abstract声明,抽象方法默认是open的,不再举例了。

        相对于Java中的权限修饰符,public,private等,Kotlin中也有自己的修饰符,其中public、private与Java中的使用效果一样,不同的是,Java中的default修饰符在Kotlin中没有,Kotlin中使用了新的修饰符internal,该修饰符的效果是只在模块内部课件,这个模块就是一起编译的一组Kotlin文件,可以理解为一个项目,这样外部代码将类和你的包名一致,也无法使用该权限下的内容。protected在Kotlin中只是子类可见,不再同一个包中可见。

        另外,Kotlin中的默认声明是public的,并且允许在顶层声明为private这样该内容只能在当前文件内可见。


三、内部类和嵌套类

        和Java中相同,Kotlin也允许在一个类的内部声明另一个类,Kotlin中有两种声明的方式,这两种方式将有不同的作用。

class MyKotlinView(context: Context? = null, attrs: AttributeSet? = null
                   , defStyleAttr: Int = 0, defStyleRes: Int = 0)
    : View(context, attrs, defStyleAttr, defStyleRes) {
    var content : String = ""
    class Bean {
        val name : String
        get() = ""
    }
    
    inner class Entry{
        val name : String
        get() = this@MyKotlinView.content
    }
}

        由上面示例可以看到,两种声明的方法,区别在于第二个类声明时使用了inner关键字。

       在Kotlin中,默认声明的叫做嵌套类,嵌套类是不持有外部类的对象的,所以我们无法获取到外部类MyKotlinView的content内容,类似于Java中的静态内部类。而如果使用inner声明,就是我们Java中通常使用的内部类,将会持有外部类的对象,使用  this@外部类名  的方式获取外部类的对象


四、密封类

        当我们在Java中使用instance of 去判断一个对象是某个子类的时候,会使用if ... else if ... else这种方式,在最后总会写一个空的else,因为我们知道已经把所有子类都遍历完了。同时,如果添加了一个新的子类,又有可能在某个地方忘记这些if判断,引起Bug出现,现在,Kotlin就为这种烦恼提供了一个很好的解决方案,让我们一起看一下密封类的使用。

        Kotlin中在声明类的时候,可以使用sealed关键字,使用之后这个类就是一个密封类,然后在密封类中,将所有的子类都使用嵌套类的方式列出来。这样做之后,在使用when的时候,我们可以只写子类的分支,不再需要默认的else分支,同时,如果我们新添加了子类,那么when语句没有对应添加的话,编译会报错!这样就避免了上文中提到的风险和隐患。

        密封类的声明方式如下,使用sealed关键字后,该类默认是open的

sealed class Paper {
    class Book() : Paper()
    class Newspaper() : Paper()
}

        这样在对该类型的对象使用when循环时,不需要写最后的else分支,因为编译器已经认为我们覆盖了所有的可能性。

    fun checkPaper(paper : Paper)
    {
        when(paper)
        {
            is Paper.Book -> println("is Book")
            is Paper.Newspaper -> println("is Newspaper")
        }
    }

五、constructor和init关键字

        顾名思义,constructor就是构造方法的意思,我们可以用constructor声明不同的构造方法,虽说我们应该使用直接指定默认值的方式,让类只有一个构造方法比较好。

        init关键字后可以跟一个代码块,这个代码块就是在执行完构造方法之后执行的代码,也是我们通常在Java类中构造方法执行之后初始化该类一些数据的地方。

class Teacher
{
    constructor(name : String , age : Int)

    constructor(nickName : String, id : String)
    init {
        println("init")
    }
}

        当我们需要向Java那样使用单例模式的时候,如何声明一个私有构造方法的类呢?让我们看下面的实现方式

class User private constructor()
{   
}
        很简单,在constructor关键字前加上权限关键字private即可声明一个构造方式是私有的类。


六、接口中的抽象属性(变量)

        在Kotlin中,接口也可以拥有抽象属性,这样写的话,实现该接口的类,必须提供获取这个属性的方式,可以使用多种方法进行这个赋值,示例如下

interface User
{
    val nickName : String
}

class PrivateUser(override val nickName: String) : User //构造方法中声明

class SubscribingUser(val email : String) : User
{
    override val nickName: String
        get() = email.substringBefore("@")  //通过get方法赋值
}

class FacebookUser(val accountId : Int) : User
{
    override val nickName: String = getFacebookName()  //属性初始化的时候赋值
    
    fun getFacebookName() : String
    {
        return ""
    }
}

七、data关键字

        在Java中,对于toString(),equals(),hashCode()三个方法我们都很熟悉了,每次在建立一个数据类的时候,我们都要重写这三个方法,如果当数据类中新添加了数据,还要再次改一次,非常麻烦。

        对于这种没有重复性极高的工作,Kotlin提供了data关键字作为替代,从此我们可以告别这些额外的工作量。

        当我们对一个类声明为data类型,Kotlin就会自动帮我们生成这三个方法,简单而快捷!

data class Client(val username : String , val password : String)

        在使用data关键字后,Kotlin还会额外帮我们创建一个copy的方法,该方法可以创建一个相同的实例,修改我们传入的参数,因为为了安全性,Kotlin建议我们将存储数据的变量设置为val的,此时的copy方法就可以在需要修改某个属性的时候使用,可以讲其他的原有属性均保留下载,并且是创建一个新的实例,不会影响到原有对象在其他地方的使用。

        使用方式如下

        val client  = Client("123","123")
        val copyClient = client.copy("234") //没有标注修改的对象,默认是第一个
        println(client) //打印Client(usernam=123,password=123)
        println(copyClient)//打印Client(usernam=234,password=123)

       

八、by关键字

        对于一个已经封装好的类,我们在使用的时候,又需要在这个类上面扩展一些功能方法,特别是需要和某些东西结合在一起的时候,如我们对某一个接口进行包装。创建一个类,这个类中有一个变量类型是这个接口,其他还会有一些数据之类的变量,这种方式我们在开发中经常使用,在使用的时候,通常我们又会为需要包装的那个接口提供很多同名方法的入口,只为调用这个接口真正的方法,书写了很多额外的代码,很不方便,看起来也不美观

        而在Kotlin中,为我们提供了by关键字来解决这种需要额外书写大量同名方法的困扰。 当我们使用by关键字时,也可以对我们需要特殊定制的方法提供不同的实现,使用示例如下

class MyList(val innerList : MutableCollection<String> = ArrayList<String>()) : MutableCollection<String> by innerList
{
    var objAdded : Int = 0;
    override fun add(element: String): Boolean {
        objAdded++
        return innerList.add(element)
    }

    override fun addAll(elements: Collection<String>): Boolean {
        objAdded += elements.size
        return innerList.addAll(elements)
    }
}


九、object关键字

        在Java中我们在某些场景需要只有一个对象,这时候我们会使用单例模式,创建单例模式的时候有很多种写法,也有各种写法的评价,我们此处对于Java创建单例模式的方式优缺点不进行解释。重点是在Kotlin中,创建单例模式我们只需要使用一个关键字即可!

        在声明一个类的时候,我们只需要使用object进行声明,这个类就是单例模式的了(其实在Kotlin中认为object关键字是声明了一个对象,只是一个对象当然就是单例的了),使用object声明的对象,可以有方法,属性,初始化语句块等,唯一与类不同就是不允许建立构造方法,因为在声明的时候就已经创建了对象,外部也没有也不需要调用构造方法,因此构造方法是没有意义的。

        下面看一个示例,我们在进行集合排序的时候,会传入一个Comparator对象,这个非常适合做成一个单例的对象。

object MyComparator : Comparator<String>
{
    override fun compare(o1: String?, o2: String?): Int {
        if (null != o1) {
            return if(null != o2) {
                o1.compareTo(o2)
            } else {
                1
            }
        }
        else if(null != o2)
        {
            return -1
        }
        else
        {
            return 0
        }
    }
}

        另外,object关键字,还可以在类内部声明对象使用,此时,它在容器类中不同的实例中,仍然只有一个实例对象。

        object关键字在类的内部使用时,还可以在前面添加companion关键字,此时的object对象就成为了一个伴生对象,伴生对象中的方法,可以直接使用容器类名 +  .  + (伴生对象名 + .) +   方法名的方式调用,并且伴生对象可以调用容器类中的私有构造方法,这样我们就可以在Kotlin中实现工厂模式了,示例如下

class Fruit private constructor(val name : String){
    companion object Creater{
        fun creatApple() = Fruit("Apple")
        fun creatBanana() = Fruit("Banana")
    }
}

        调用方式如下

        val apple = Fruit.creatApple()
        val banana = Fruit.Creater.creatBanana()

        tips:当我们不为伴生对象命名的时候,会有默认的名称Companion

        伴生对象同样也可以实现接口和为伴生对象创建扩展函数,此处不再过多介绍,大家可以自己试一下。


        在Android开发中,有太多太多的匿名内部类,那么在Kotlin中,我们就可以用刚认识的object关键字来创建匿名内部类,让我们看下面示例

        val txt = findViewById(R.id.txt) as TextView
        txt.setOnClickListener(object : View.OnClickListener{
            override fun onClick(v: View?) {
                println("txt onClick")
            }
        })

            可以看出,我们使用了object声明了一个匿名对象,实现了onClickListener接口,然后实现了onClick方法,这样就创建了一个匿名内部类,使用起来很简单。


        //下一章  新手上路,Kotlin学习笔记(四)---Lambda表达式在Kotlin中的使用

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值