书接上篇……
之所以将这三个写在一起而不与类写在一起,大概是因为这三者惺惺相惜吧。(类写完了才发现,如果一个开一篇文章的话……有划水的赶脚!嘿嘿)
在面向对象中这继承、抽象类、接口
三者可是占着不弱的低位哈,可以说有了它们,面向对象才具备了灵魂。
在上篇文章我说了类的定义,对象的创建,其实都没什么,但是!!构造方法的定义与使用一定要摸清楚。因为,在用惯了java后对于接下来的继承、抽象类、接口难免会觉得绕。
1. 继承
一. 基本继承
首先来说说继承!
来一段代码:
package cn.xinidi.java;
/**
* @author Gang
* @Date 2020/4/29
* @Version 1.0
*/
public class JavaFile {
public static void main(String[] args) {
Man man = new Man();
System.out.println(man.head);
}
}
class Person{
String head = "头";
}
class Man extends Person{
public Man() {
this.head = "男人的头";
}
}
这是java中的继承。
来看kotlin中的继承:
package cn.xinidi.kotlin
/**
* @author Gang
* @Date 2020/4/29
* @Version 1.0
*/
class KtFile {
companion object{
@JvmStatic
fun main(args: Array<String>) {
println("kt:${Man().head}")
}
}
}
open class Person {
var head = "头"
}
class Man : Person() {
init {
head = "男人的头"
}
}
好了,这就是基本的继承了。
要点:java没什么可说的。然鹅,对于kotlin来说,父类 Person
使用了一个open
关键字,以达到子类可以继承它。(你也可以试试去掉open
)
子类继承的关键字并不是extends
而是一个:
,其实这个:
在kotlin中还有其他用法,不要急,马上会说。
接下来说方法的继承,子类重写父类方法:
open class Person {
var head = "头"
open fun jump() {
println("跳!")
}
}
class Man : Person() {
init {
head = "男人的头"
}
override fun jump(){
println("男人在跳!")
}
}
父类方法jump
也是用了open
来声明,该方法可以被继承重新,但是在子类中我们在jump
前加了一个override
。说起override
,我相信在java中没少见它吧。
在java中随你自己的心情写或者是不写它,编译器不会提醒你的代码有问题。
但是在kotlin中,你必须写它,不写就报错,不给糖就捣蛋。
同样的,重写父类字段也是通过override
open class Person {
open var head = "头"
open fun jump() {
println("跳!")
}
}
class Man : Person() {
override var head = "男人的头"
/*init {
head = "男人的头"
}*/
override fun jump() {
println("男人在跳!")
}
}
二. 父类的构造函数
如果继承的父类带有参数构造函数,那么该怎么继承呢?
open class Animal(var type: String) {
……
}
class Dog(name: String, type: String) : Animal(type) {
……
}
这是在子类拥有主构造函数的情况下,如果没有主构造函数,那么次构造函数怎么写呢?
open class Animal(var type: String) {
……
}
class Cat : Animal {
constructor(name: String, type: String) : super(type) {
……
}
constructor(name: String, age: Int, type: String) : super(type) {
……
}
}
子类中可使用super
调用父类的方法。
与java一样,kotlin只支持单继承
这里又一点要注意:
子类继承父类时,不能有跟父类同名的变量,除非父类中该变量为 private,或者在父类中该变量为 open 并且子类是用 override 关键字重写的。
2. 抽象类
类的继承说完了,那么接下来就是今天的第二个猪脚。
与java一样,抽象类的声明也是使用abstract
abstract class AbsKtPerson {
open abstract fun run()
open abstract fun jump()
}
继承实现:
3. 接口
kotlin中的接口也是用interface
来定义
interface InterfacePerson{
open fun eat()
open fun speak(){
println("说话!")
}
}
interface InterfaceTool{
open fun knife()
}
class Impl : AbsKtPerson(),InterfacePerson,InterfaceTool{
override fun run() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun jump() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun eat() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun knife() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
同时在kotlin中,接口中支持写方法体,实现了方法体的方法,子类可以选择性实现。
override fun speak() {
super.speak()
}
如果多个上级中存在了相同的已实现的方法,如:
abstract class AbsKtPerson {
open abstract fun run()
open abstract fun jump()
//在抽象类中实现了
open fun speak(){
println("AbsKtPerson说话!")
}
}
interface InterfacePerson{
open fun eat()
//在接口中实现了
open fun speak(){
println("InterfacePerson说话!")
}
}
将会出现以下错误
你可以通过泛型指定使用其中一个的super
(但是,该super
除了某些特定情况下必须由子类调用父类,其他情况下并不是必须的)。
也就是你可以这样:
但是遇到必要调用的时候:
这里我要来说一个容易混淆的地方
也就是上文提到的:
在子类中 无论是实现接口,还是继承父类都是使用的:
那么区别在于
对,就是这个()
。如果是继承类,只需要在后面加一个括号(别傻啊,这括号是干嘛的?继承哪块已经说了,负责将父类需要的参数传过去,如果父类有一个参数构造函数,那就不能直接打括号了)
另外还有一种写法,可以稍微记一下
好了就到这,这些东西我只讲了个大概,并不是我讲了,你看了,就完了。
要自己多写多练,另外kotlin中的多态与java中是一样的!
4. 关于“::”引用的说明
在java中我们可以通过接口实现方法回调,特别是在android这一块使用得较多。
在kotlin有一种写法,就是将方法作为参数传给需要被调函数。
……是不是听起来有点绕?没关系,看代码就是了
上图中首先创建了一个名为t
的KtTest对象,然后通过t::func
传给了show
方法
show方法中的参数是一个名为funcM
的参数名,类型为()->Unit
()->Unit
表示一个返回空的方法。
如果不好理解,可以这么看
运行结果:
*注意:如果对象没有创建(new),那么不能直接传方法,至于为什么?对象都没有创建,虚拟机怎么知道该调用谁的方法。
5. 总结
- kotlin中,继承需要父类使用
open
关键字,子类实现则需要以override
关键字,不能省略 - kotlin中,抽象类与java中几乎一样。
- kotlin中,接口与抽像类一样可以有方法实现,子类继承可以选择性覆写已经实现的方法
- kotlin中,如果子类的上级存在
两个已经实现的相同方法
可以通过super<泛型>.方法名
指定使用上级的方法,但不是必须要写。 - kotlin中,继承与接口的实现都是用的
:
表达式,区分的方式就是()
,也就是子类继承父类相当于创建了一个父类对象,然而接口则不需要。 - kotlin中,支持函数(方法)作为参数传递,使用
::
表达式