【基础篇】Kotlin的类、对象和接口

【基础篇】Kotlin第四章

类是逻辑组织的基本单元,类含有以下成分:依赖包,类名,构造方法,属性,成员方法,伴生对象,接口,父类等

类的构造方法的完整逻辑过程

先考虑主构造函数,当主构造函数不够用时,再引入从构造函数。最初的主构造函数是这样的:步骤1通过主构造函数的参数传入数据,步骤2在类里定义所需的属性,步骤3在init代码块里对属性做初始化/赋值操作,这三个步骤分工明确,前后关联,共同完成了一个类的构造。

为了简化,Kotlin支持步骤2和步骤1合并操作,在主构造函数参数前加var/val;

支持步骤3和步骤2合并,对定义的属性直接赋值。支持步骤3和步骤2和步骤1合并操作,使用主构造函数的默认参数表达。

当然这些合并带来简便的同时,降低了构造的能力。

最初的状态(步骤1,步骤2,步骤3都存在)

class User(_sickName: String){

    val sickName: String

    init {
        println("init")
        sickName = _sickName
    }
}

步骤2和步骤1的合并,在主构造函数里传参和定义类的属性

class User(val sickName: String){

    init {
        println("init")
    }
}

步骤3,步骤2和步骤1的合并

open class User private constructor(val sickName: String = "Kotlin", var age: Int){
    
    init {
        println("init")
    }
}

注意:Java中需要重载构造方法的场景大多数都被Kotlin参数默认值和参数命名的语法特性涵盖了

子类的构造方法

open class AirBook(name: String, val year: Int, val size: Int) {

   constructor(name: String, year: Int) : this(name, year, 22){
       println("constructor")
   }
}

class MacBookPro: AirBook{

   public constructor(name: String, year: Int) : super(name, year) {
   }

   public constructor(name: String, year: Int, size: Int) : super(name, year, size){
   }
}

子类在创建的时候,必须调用父类的构造函数,如果其父类还有父类,仍然要调用父类的父类的构造函数,直至顶层的基类。

open class AirBook(name: String, val year: Int, val size: Int){

    init {
    }

    constructor(name: String, year: Int) : this(name, year, 22){
    }
}

构造函数可以调用当前自身的构造函数,因为自身的构造函数必然有至少一个调用了父类的构造函数。

属性访问器

在kotlin里,一个属性 = 字段 + 属性访问器;这里的字段是Java里的成员变量

属性访问器分为读访问器getter,和写访问器setter

访问器里存在field字段,用来连接写访问器和读访问器,作为两者的通信桥梁

val修饰的属性只有读访问器,var修饰的属性既有有读也有写访问器

Kotlin引入属性访问器后,凡事对属性进行赋值操作,就会调用属性的写访问器setter;读取值的操作对应调用的是属性的读访问器

那么如果我们要自定义一个类似Java里的setter方法,要怎么做呢?

通过var变量的私有setter来实行

代码如下:

class TableLamp(val name: String, lightness: Int){

    var lightness: Int = 1
    private set
    
    fun setupLightness(lightness: Int){
        this.lightness = lightness
        println("setupLightness lightness = $lightness")
    }
}

这样对属性lightness的修改,只能通过setupLightness方法。

修饰词final, open, abstract相互影响和使用

Kotlin的函数,类默认是final的,如果想重写函数,类有子类,则需使用open修饰。abstract修饰的函数和类,意味着有open的特性。这和Java的用法是一致的。

Kotlin自带的特殊类

数据类,嵌套类,内部类,密封类的基本写法这里就略去不谈。

数据类data

data修饰的类,必须有主构造函数,且主构造每个入参必须都val或var修饰

data修饰补充的方法里的所用的变量取决于主构造函数的入参

嵌套类和内部类

定义在某个类内部并用inner修饰的类称为内部类

嵌套类可以类比Java的静态内部类,Kotlin的内部类类比Java的内部类,他们含有外部类的引用。与Java内部类不同的是,Kotlin的内部类能对外部类的变量进行写操作。

举个例子

class TextView{
    var counter: Int= 0
    
    inner class Operator{
        fun calculate(){
            counter ++
        }
    }
}

内部类Operator的成员函数可以写外部类的counter属性,这在Java里是做不到的。

密封类

sealed关键词修饰的类,表达有限个子类。

sealed class Color constructor(val name: String)

class Red : Color("Red")

class Green : Color("Green")

class Blue : Color("Blue")

class Gray : Color("Gray")

上述写法,编译器就会知道,Color的子类的数量,此例Color一共只有四个子类,分别是:Red,Green, Blue, Gray。如果增加或删除Color的子类,编译器是能感知到的。密封类的子类可以作为嵌套类,也可以写在密封类所在的文件里,但是不能写在其他文件里。

举个和When搭配的应用例子:

fun testSealed(color: Color): Int =
        when (color) {
            is Red -> {
                println("Red")
                1
            }
            is Green -> {
                println("Green")
                2
            }
            is Blue -> {
                println("Blue")
                3
            }
            is GRAY ->{
                println("GRAY")
                4
            }
        }

密封类的意义:
如果不用密封类,使用when总是不得不添加一个默认分支。更重要的是,如果你添加了一个新的子类,编译器并不能发现有地方改变了。如果你忘记了添加一个分支,就会选择默认的选项,这可能导致潜在的bug。

使用密封类就能解决上述的问题。

Object的使用

声明一个类并创建一个对应的实例。与类一样,对象可以包含属性,方法,初始化语句块等的声明。对象声明可以继承类和实现接口,尤其不包含状态的时候很适用,也可以有扩展函数。

对象声明

创建单一的实例,对象声明在定义的时候就创建了,不需要在代码的其他地方调用构造方法。

因此,对象声明不允许有构造函数。

object NameComparator : Comparator<String>{

    override fun compare(o1: String, o2: String): Int {
        return o1.compareTo(o2, true)
    }
}

Java调用上述代码,则需要通过INSTANCE来调用,如下

 CaseInsensitiveFileComparator.INSTANCE.compare("abc", "abe");

伴生对象

创建单一的实例,可以实现Java里访问类的私有成员的静态方法

在对象声明的基础上,使用companion关键字来标记,这样做就获得了直接通过容器类名称来访问这个对象的方法和属性的能力。

举个例子,实现CarFactory工厂

通过对象声明的实现

class CarFactory{
    object Instance{
        fun newCar(name: String): Car{
            return Car(name)
        }

        fun newRedCar(name: String): Car{
            return Car(name, Red())
        }
    }
}

调用

  CarFactory.Instance.newCar("Mini Cooper")

通过伴生对象的实现

class CarFactory{
    companion object {
        fun newCar(name: String): Car{
            return Car(name)
        }

        fun newRedCar(name: String): Car{
            return Car(name, Red())
        }
    }
}

调用

CarFactory.newCar("Mini Cooper")

对象表达式

既然是表达式,就意味着有返回值

与Java的匿名内部类只能扩展一个类或者实现一个接口不同,Kotlin的匿名对象可以实现多个接口或者不实现接口。

与对象声明不通,匿名对象不是单例的。每次对象表达式被执行都会创建一个新的对象实例。在对象表达式里不经可以访问创建它的函数中的变量,还可以修改变量的值

举个例子

fun countClick(view: View){
    var clickCount = 0
    
    view.addClick(object : IClick{
        
        override fun onClick() {
            clickCount ++ 
        }
    })
}

小结

object对象在Kotlin中的意义:

  1. 实现Java里静态的功能,等效实现静态调用
  2. 代替Java匿名内部类书写

接口

interface IFocus {
    val focusName: String

    fun showOff(){
        println("IFocus foucusName length = ${focusName.length}")
    }

    fun onFocus()
}

IFocus接口声明函数,让子类实现,默认方法showOff,和抽象方法onFucus

class View : IFocus{

    override val focusName: String
        get() = "View"

    override fun onFocus() {
        showOff()
    } 
}

View实现IFocus接口,focusName由View来确定,获取focusName逻辑写在IFocus的默认方法里。调用如下

val view = View()
view.onFocus()

调用返回内容

IFocus foucusName length = 4

其他

访问权限修饰词

protected在Kotlin和Java的区别
kotlin中protected只能是其子类和自身才能访问;Java中则是同包下所有文件和不同包的子类能访问

Kotiln访问权限由小到大排列依次是:
private, protected, internal, public

Kotlin函数和属性默认是public的,Java的默认是包级访问范围,即同一个包下的类能访问。
Kotlin缺少包级别访问控制,而多了一个模块访问范围internal。internal表示同一个项目模块下的类都能访问

参考资料

Kotlin实战

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

推荐阅读更多精彩内容