Kotlin面向对象编程

1、类与对象

Kotlin中类的声明和Java类一样,都是用关键字class来声明类

class Person {
    var name = ""
    var age = 0

    fun introduce() {
        Log.e("TAG", "我的名字叫" + name + " 我已经" + age + "岁了")
    }
}

Person类已经定义完成,那如何创建Person类的对象呢?

 val p = Person()

和Java类似,只是剩去了new,这样也很好理解:Person()代表的是构造函数,而只有创建对象时才会调用构造函数,这样编码的意图也很明显就是创建对象。
接下来我们就能使用对象进行一些操作:

fun createPerson() {
        val p = Person()
        p.age=100
        p.name="张丹"
        p.introduce()
    }

2、继承与构造函数

  • 1、在Kotlin中非抽象类、非接口默认是不能被继承的,要想某个类能被继承,就需要使用关键字open
open class Person{
}
  • 2、使用:来表示继承关系
class Student :Person() {
}

细心的你肯定也发现了Person后加了一对小括号,这又是为什么呢?这就和Kotlin中的主构造函数和次构造函数有关系了,

2.1、主构造函数

  • 1、Kotlin中每个类都默认有一个无参的构造函数来作为主构造函数,而且主构造函数有且仅有一个,次构造函数的个数不限。
  • 2、我们也可以给主构造函数显示的指定参数
class Student(val sno: String, val grade: Int) : Person() {
}

我们将学号和年级在主构造函数中声明了,所以在创建Student对象的时候就必须传入主构造函数中要求的所有参数。

  • 3、主构造函数没有方法体,直接在类名后定义即可。
    如果我想在主构造函数中写一些逻辑该怎么办呢?Kotlin给我们提供了init结构体,所有主构造函数中逻辑可以写在结构体中。
class Student(val sno: String, val grade: Int) : Person() {
    init {
        //在创建Student对象时就会执行这个结构体
        Log.e("TAG","我是主构造函数,学号sno:"+sno+" 年级grade:"+grade)
    }
}
  • 4、在java中子类的构造函数必须要调用父类的构造函数,在Kotlin中也是如此。上面在继承Person时Person后面加了一对括号的用处就是在创建Student对象时指定在Student的构造函数中调用父类的那个构造函数。
    下面我们对Person修改一下,将name和age放在主构造函数中
open class Person(val name: String, val age: Int) {
}

此时Student类肯定会报错,因为在Student类中我们指定调用父类的无参构造,而无参构造已经不存在了,必须给Person类传入name和age。可是在Student中并没有name和age,我们可以在Student中加入name和age

class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) {
}
  • 5、注意:在Student类的主构造函数中加入的name和age不能使用val或var修饰,因为在主构造函数中使用val或var修饰的参数将自动变成该类的字段,这就导致和父类中的name、age同名导致的冲突。

2.2、次构造函数

  • 1、其实在Kotlin中我们一般用不到次构造函数,因为Kotlin中提供了给函数设定参数默认值的功能,基本可以替代次构造函数。
  • 2、一个类只有一个主构造函数,但是可以有多个次构造函数。和主构造函数不同的是,次构造函数是有方法体的。
  • 3、Kotlin规定:当一个类中既有主构造函数又有次构造函数时,所有的次构造函数必须调用主构造函数(包含间接调用)
class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) {

    init {
        Log.e("TAG", "我是主构造函数,学号sno:" + sno + " 年级grade:" + grade)
    }

    constructor(name:String,age:Int) : this("",0,name,age) {

    }

    constructor():this("",0){

    }

}
  • 4、次构造函数是通过关键字constructor定义的,这里我们定义了两个次构造函数:第一个次构造函数传入name和age作为参数,然后通过this关键字调用了主构造函数;第二个次构造函数没有参数,它通过this关键字调用了第一个次构造函数间接的调用了柱构造函数,这也是合法的。现在我们就有3种方式创建Student的实例了
val stu1=Student("张无忌",50)
stu1.introduce()

val stu2=Student()
stu2.introduce()
        
val stu3=Student("10010",5,"尹力",130)
stu3.introduce()
  • 5、如果一个类中没有显式的定义主构造函数,而使用关键字constructor定义了次构造函数,那么类中是没有主构造函数的
class Teacher : Person {
    constructor(name: String, age: Int) : super(name, age) {
    }
}

注意这里代码的变化,Teacher类中没有显式的定义主构造函数并且定义了次构造函数,所以Teacher类是没有主构造函数,由于没有主构造函数,所以在继承Person类时也就不需要加扩号,原因很简单:由于Teacher类没有主构造函数,所以我们不能通过主构造函数来创建Teacher的实例,即使Kotlin允许此时添加括号,也是无法达到调用父类构造的目的。我们只能通过次构造函数创建实例,我们只能通过super来调用父类的构造函数。

3、接口

Kotlin中和Java一样都是单继承的,一个类只能继承一个父类,但是可以实现多个接口。和Java一样也是通过interface声明接口

interface Study {
    fun readBook()
    fun doHomeWork()
}

Student类实现Study的接口,并实现其中的方法

class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age),Study {

    init {
        Log.e("TAG", "我是主构造函数,学号sno:" + sno + " 年级grade:" + grade)
    }

    constructor(name: String, age: Int) : this("", 0, name, age) {

    }

    constructor() : this("", 0) {

    }

    override fun readBook() {
    }

    override fun doHomeWork() {
    }
}
  • 1、Kotlin中实现接口和集成一样都是使用:,类和接口间使用,隔开,另外接口的后面不需要加括号,因为接口根本就没有构造可以被调用
  • 2、Kotlin中使用关键字override来重写父类的方法或实现接口中的方法,另外如果父类中的方法需要被重新的话,需要在方法前增加关键字open,否则无法重写。
  • 3、Kotlin中的接口可以有默认的实现,接口中有默认实现的方法子类可以不实现。
interface Study {
    fun readBook(){
        Log.e("TAG","接口中的方法可以默认实现")
    }
    fun doHomeWork()
}

4、修饰符

在Kotlin中函数的可见性修饰符相比于Java变动还是很大的。直接上表

修饰符 Java Kotlin
public 所有类可见 所有类可见(默认修饰符)
private 当前类可见 当前类可见
protected 当前类、子类、同一包下的类可见 当前类、子类可见
default 当前类、统一包下类可见
internal 同一模块下可见

5、单例和数据类

5.1、数据类

数据类通常要重写equals()、hashCode()、toString()几个方法,其中eaquals()判断两个数据类是否相等,hashCode()作为eqauls()的配套方法,也需要重写否则会导致HashMap、HashSet中相关hash的操作不能正常工作,toString()是为了提供了一个清晰的日志输出,否则打印出来就是内存地址。下面举个简单的例子:

public class CellPhone {
    String brand;
    double price;

    public CellPhone(String brand, double price) {
        this.brand = brand;
        this.price = price;
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof CellPhone) {
            CellPhone cellPhone = (CellPhone) o;
            return cellPhone.price == price && cellPhone.brand.equals(brand);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return brand.hashCode() + (int) price;
    }

    @Override
    public String toString() {
        return "CellPhone:brand:" + brand + " price:" + price;
    }
}

在Kotlin的实现上述功能就只需一句代码即可。

data class CellPhone(val brand: String, val price: Double) 

神奇的地方就在于data关键字,在Kotlin中的类声明了data关键字,那么这个类就是一个数据类,Kotlin中会根据主构造函数中的参数自动生成eqauls()、hashCode()、toString()等方法并实现相应的逻辑。另外在一个类中没有任何代码时,可以省了大括号
下面我们测试下该类

fun testDataClass() {
        val cellPhone1 = CellPhone("xiaomi", 2000.0)
        val cellPhone2 = CellPhone("xiaomi", 2000.0)
        Log.e("TAG", "cellPhone1:" + cellPhone1.toString()+"  cellPhone2:"+cellPhone2.toString()+"  cellPhone1.equals(cellPhone2):"+(cellPhone1==cellPhone2))
    }

输出:
cellPhone1:CellPhone(brand=xiaomi, price=2000.0)  cellPhone2:CellPhone(brand=xiaomi, price=2000.0)  cellPhone1.equals(cellPhone2):true

很明显CellPhone已经正常工作了。

5.2、单例类

在Java中的实现

public class SingleInstance {
    private static SingleInstance instance;

    private SingleInstance() {
    }


    public static synchronized SingleInstance getInstance() {
        if (instance == null) {
            instance = new SingleInstance();
        }
        return instance;
    }

}

在Kotlin中创建一个单例类很简单,只需要将class关键字改成object关键字即可。

object SingleInstance {
}

现在SingleInstance就是一个单例类了,我们可以直接在这个类中编写需要的函数,比如加入一个singleInstanceTest()

object SingleInstance {

    fun singleInstanceTest() {
        Log.e("TAG","singleInstanceTest is called")
    }
}

可以看到,Kotlin不需要私有化构造函数,也不需要提供getInstance()这样的静态函数,只需要将class修改为object,Kotlin就帮我们生成了单例类相关的逻辑,它的调用也很简单

SingleInstance.singleInstanceTest()

这种写法看上去像是静态方法的调用,其实Kotlin内部已经帮我们生成了SingleInstance实例,并且该实例有且仅有一个。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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