前置知识
- 已学习 Kotlin 变量、函数知识
- 有
Java
编程基础
前言
本篇作为 Kotlin
基本功的倒数第二篇,将为大家带来关于 Kotlin
中类和继承的知识。如果你看到这篇文章的时候,还未听说过 Kotlin 或者从未学习过 Kotlin ,你可以点击链接从本系列的第一篇文章开始学习。在本系列前面的文章中,我们学习到了关于 Kotlin 的函数、变量还有逻辑控制的知识;而当你学完本篇后,你掌握了 Kotlin 中类和继承的知识,你就是学会了 Kotlin 中大部分的知识了。
话不多说,赶紧学起来吧!
Kotlin的OOP VS Java的OOP
OOP 就是面向对象的缩写。在 Java 中,其三大特性分别是 封装、继承和多态。如果不太记得三大特性是什么了,可以查看我的另一个专栏——设计模式专栏的文章,里面有简单的总结了三大特性。那么在 Kotlin 中的面向对象和 java 的面向对象有什么区别呢?我在这里言简意赅的回答你,特性和理论是一样的,但使用方法不一样。那么,使用方法上有何不同呢?下面听我细细道来,我们简单的创建一个类和继承对应的类,用这个实战的过程带你学明白 Kotlin 的类。
ps:封装在前文已做讲解。
Kotlin 类和普通创建
要了解一个东西,我们需先窥其全貌。所以,我们可先看一下在 Kotlin 中类是长什么样子的。
从上述的截图中,我们可以看到,Kotlin 的类可以直接以 class
来声明,这点和 java
是一致的。但是上述的代码也有很多不一样的地方,譬如类后面和函数一样带有括号,且还有冒号用于继承等等这些我们 Java 开发者看来十分陌生的东西。
但我们对此不必害怕,我们学习完本文之后再来重新阅读就会一目了然了。
创建一个类
我们尝试自己创建一个 Kotlin 的类。创建步骤和创建 Java 类一样,只是最后选择 Koltin 文件即可。下面我们立刻来实战一下,从实战中学习。
第一步,我们定义一个空的类 ViewTest
。可以是如下这样子的。
class ViewTest{ } 复制代码
第二部,我们向类里面添加属性和方法。最后去主函数中初始化这个类和调用其中的函数,这样子就是简单的创建和使用一个类了。这和 Java 没太大不同的。
class ViewTest{ val string1 = "s1" val string2 = "s2" fun print(){ println(string1 + string2) } } fun main() { val v = ViewTest() v.print() } 复制代码
但是上面的代码中,类的初始化和 Java 中有些不一样,你会发现这里无需在初始化的时候加 new
关键字,而其他都是一样的。下面附上代码的运行结果。
Kotlin 中的继承
下面我们继续学习它的继承特性。
在 Java
中,默认所有类都是可以被继承的,但是 Kotlin
中不一样,Kotlin
的设计遵循 多用组合少用继承 的思想,默认类不可被继承。
如果类需要被继承,那么我们就需要在 class
前加一个 open
关键字。例如,我们想要使得上面的 ViewTest
类可以被继承,那么我们需要做如下修改。
open class ViewTest{ val string1 = "s1" val string2 = "s2" fun print(){ println(string1 + string2) } } 复制代码
这样子, ViewTest
类就可以被继承了。说明一下,Kotlin
中继承使用的关键字是**:**。其次,我们创建一个子类继承这个 ViewTest
父类。然后运行这段代码。
你会惊奇的发现,程序运行出错啦!这是怎么回事呢?
上述的代码出现了一行红色的错误信息,这句错误信息翻译过来是:此类型具有构造函数,因此必须在此处初始化。
这时候,我们会懵圈了,构造函数?默认构造函数不是为空的吗?此处没有新添加构造函数,那么就默认调用父类空的构造函数才对呀!
确实,在 Java
中我们的确可以这样子理解,但是在 Kotlin
中,却并非如此。Kotlin
中的构造函数和 Java
的有很大的不同。
接下来我们继续学习 Kotlin
的构造函数,顺便解决这个bug。
构造函数
在 Kotlin
中,构造函数分为 主构造函数 和 次构造函数,而一般情况下我们只需要使用到 主构造函数。
主构造函数
Kotlin
的主构造函数也默认的是一个无参的函数,所以在首次执行 val v = ViewTest()
的时候是成功可行的。
而在 Kotlin
中,子类继承父类的时候,和 Java 一样是需要调用父类的构造函数的,但是 Kotlin
不会帮助你调用默认的主构造函数,如果你需要使用主构造函数,就需要手动的去声明继承父类的构造函数。
在上面子类的代码中,我们只是使用冒号:继承了 ViewTest()
这个类,却并未声明我们的类需要初始化父类的主构造函数是怎么样的。至于为何一定要声明,那是因为父类的主构造器参数可以是多样性的,不做声明就无法确定调用的父类主构造函数是怎么样的,不知道它有几个参数。所以我们直接在 ViewTes
t 后加上(),声明我们调用的父类的构造器是默认的,未改造的,就不会出错了。
class ViewSon : ViewTest(){ } 复制代码
通过上面的结果图,我们可知这个bug解决了。
那么,主构造器可以如何多样化呢,这时候,我们就对其父类()内的参数进行扩增,就可以实现其多样化了。
将参数放到括号内,就是实现了构造函数属性初始化。
open class ViewTest(val string1: String, val string2: String){ fun print(){ println(string1 + string2) } } 复制代码
然后,我们继续使用子类继承它。下方代码中,只有独属于本类的,才可以用 val
和 var
关键字来修饰,属于父类的不做修饰。而声明父类的主构造器的时候,直接传入参数名即可。
class ViewSon(val s0: String, s1: String, s2: String) : ViewTest(s1,s2){ } 复制代码
最后,我们来运行一下查看结果。
主构造函数就学到这里了。下面我们继续看次构造器。
次构造器
次构造器可以对主构造器做一个拓展,就像 Java 中,我们有多个构造器那样子。
此处直接上代码。
class ViewSon(val s0: String, s1: String, s2: String) : ViewTest(s1,s2){ constructor(s1: String, s2: String): this("S0",s1,s2){ } constructor(s1: String): this(s1,"s2"){ } constructor(): this("s1") } 复制代码
上面代码中的次构造器,使用的是 constructor
关键字+ this
关键字来层层调用来实现多个构造函数。我们可以运行一下查看效果。
有一个知识点需要说明,那就是 只有次构造器的情况是存在的,但不常见,我们也可以使用这个特点来解决前文的bug。用子类的次构造器声明使用父类哪个构造器。在这种情况下,我们就需要使用 super
关键字来解决声明父类构造器了。
class ViewSon : ViewTest{ constructor():super() } 复制代码
今天学习的就到这里了,加油!