文章目录

  • 1、抽象类
  • 2、接口
  • 3、接口代理

在期待着郭霖先生的《第一行代码(第三版)》时,意识到自己需要补充必要的Kotlin知识,现在通过写博客进行分享,争取拿到书之后早日上手。
使用的软件是IDEA 2019,文章中如有错误或者欠缺的地方,欢迎批评指正。
(参考书为《Kotlin从零到精通Android开发》,欧阳燊著,清华大学出版社2018年4月第一版,ISBN 978-7-302-49814-8)。感谢欧阳先生的优秀教材。

1、抽象类

抽象类与普通的类的区别是,其中有被关键字abstract修饰的方法,被称之为抽象方法,相应的,这个类就成为了抽象类(也要声明为abstract)。抽象方法没有方法体,只是给出了参数列表和返回值,这个方法需要在抽象类的子类中实现。另外,因为抽象类是必须要继承才可以被子类用的,它自己无法构造对象,因此,抽象类的open关键字和抽象方法的open关键字可以省略。
根据之前写好的Animal类代码,准备好另一个抽象类,继承自Animal类,起名为Dog类:

abstract class Dog (name:String?,age:Int?, var voice:String?) : Animal(name,age) {init {voice="wang"}abstract fun callOut()
}

Dog类主构造函数中的name和age都是原Animal类的参数(不用带var或者val),又新增了一个成员变量voice。在Dog类的init函数中,为voice赋值为"wang"。
Dog类有一个抽象方法(抽象类也可以没有抽象方法,但这样就没什么意义了)callOut,我们的目的是为了输出狗的叫声。
接着准备好另一个类MaleDog,继承该抽象类,重写callOut方法:

class MaleDog (name:String?,age:Int?, voice:String?) : Dog(name,age,voice) {override fun callOut() {println(voice)}
}

注意,MaleDog类可以不把init方法写出来,系统默认就有init方法,因为MaleDog和父类Dog的参数列表一致,构造MaleDog对象时,调用的实际上是Dog的构造函数。如果要写init方法,也要注意父类的属性不能在子类的init方法中赋值,否则编译器报父类参数不能被再初始化错误。 父类的参数如果子类可见,则可以在子类的其他函数中修改。
callOut方法重写也很简单,注意这里override关键字需要加在fun关键字前面,重写的函数输出了voice。
可以自己尝试一下。
一般的,抽象类提供一个或多个抽象函数,不同的类都可以去继承这个抽象类,然后在自己的类中重写抽象类中的抽象函数,加入自己的逻辑,这样的做法是比较好的,体现出了继承的基本思想。

2、接口

Kotlin引入接口的思路和Java是类似的,即解决无法多重继承的问题,同时规避多重继承带来的各种问题。虽然Kotlin类是无法多重继承(即有多个父类),但是却可以继承多个接口。
什么是接口呢?接口其实功能上就相当于是抽象类,接口中可以有成员方法,可以有成员属性。这些与抽象类都是一致的。但是,抽象类中的属性默认是不抽象的,除非显式加入abstract关键字,否则在抽象类中声明的属性必须被初始化(不抽象)。

接口中所有属性、方法 都是可继承、抽象的,并且接口不能带构造函数(不然就成了类了)。接口采用关键字interface声明。同时,Kotlin的接口允许在接口内部实现方法。

在原来的Dog、MaleDog的基础上,准备一个接口ActionMethod,指定狗行动的方法,一种是走,另一种是跑,并让MaleDog继承。代码如下:

abstract class Dog (name:String?,age:Int?, var voice:String?) : Animal(name,age) {init {voice="wang"}abstract fun callOut()
}class MaleDog (name:String?,age:Int?, voice:String?) : Dog(name,age,voice),ActionMethod {override fun callOut() {println(voice)}override fun run() {println("狗在跑")}
}interface ActionMethod {//内部实现的方法,不能加abstract关键字fun walk() {println("狗在走")}//内部未实现,是抽象的方法,默认是abstractfun run()
}

可以看到,Kotlin将Java中的implementextends关键字都用冒号代替了,因此接口也加在冒号后面,用逗号与抽象类Dog区别开来。
ActionMethod在内部实现了walk方法,因此在MaleDog类中无需被重写,但是run方法没有实现,MaleDog就要重写该方法。
这里要注意的是不要把重写(override)和重载(overload)弄混了。

我们补上主函数检查一下调用情况:

abstract class Dog (name:String?,age:Int?, var voice:String?) : Animal(name,age) {init {voice="wang"}abstract fun callOut()
}class MaleDog (name:String?,age:Int?, voice:String?) : Dog(name,age,voice),ActionMethod {override fun callOut() {println(voice)}override fun run() {println("狗在跑")}
}interface ActionMethod {//内部实现的方法,不能加abstract关键字fun walk() {println("狗在走")}//内部未实现,是抽象的方法,默认是abstractfun run()
}fun main() {var adog = MaleDog("小黑",3,"wang")adog.run()adog.walk()
}

输出结果:

狗在跑
狗在走

3、接口代理

继续来思考问题,举出欧阳先生书上的例子,现有一个抽象类Bird,要实现大雁、火鸡等具体的鸟的类,除了继承Bird外,根据鸟的习性,定义一个接口Behavior,实现了鸟的各种行为;但是鸟是有习性的,比如火鸡不擅长飞,接口中的fly方法对于火鸡来说就不是必须的,这就造成了重复实现接口的问题。于是想到通过Bird类和Behavior联合实现三个抽象类,即飞禽类、水禽类、走禽类,不同的鸟类根据习性分别继承这三个类。
这样貌似解决了问题,但是问题又出来了:不同的场景下,同一只鸟可能表现出不同的行为,例如大雁既擅长飞行又擅长游泳,抽象类的办法就不管用了。
为了解决这样的问题,Kotlin给出了接口代理的办法,即一个类先声明继承自某个接口,但不直接实现该接口的方法,而是把已经实现了该接口的类作为参数传过来用。
这样说或许不清楚,直接看一个比较简单的例子,在之前的代码的基础上,为ActionMethod增加fly方法,接着定义两个行为类ActionFly和ActionMove,实现ActionMethod接口。

interface ActionMethod {//内部实现的方法,不能加abstract关键字fun walk() {println("狗在走")}//内部未实现,是抽象的方法,默认是abstractfun run()fun fly()
}class ActionMove:ActionMethod {override fun run() {println("狗在跑")}override fun fly() {println("狗不能飞")}
}class ActionFly:ActionMethod {override fun run() {println("能飞就不要跑")}override fun fly() {println("狗在飞")}
}

可以看到,在两种行为类下,同名方法有不同的行为。
接着,定义一个SuperDog类,继承自Dog类,让狗能跑能飞。给出完整的代码:

abstract class Dog (name:String?,age:Int?, var voice:String?) : Animal(name,age) {init {voice="wang"}
//    abstract fun callOut()
}
interface ActionMethod {//内部实现的方法,不能加abstract关键字fun walk() {println("狗在走")}//内部未实现,是抽象的方法,默认是abstractfun run()fun fly()
}class ActionMove:ActionMethod {override fun run() {println("狗在跑")}override fun fly() {println("狗不能飞")}
}class ActionFly:ActionMethod {override fun run() {println("能飞就不要跑")}override fun fly() {println("狗在飞")}
}
//超狗类
class SuperDog(name: String?,age: Int?,voice: String?,action:ActionMethod):Dog(name,age,voice),ActionMethod by action {}fun main() {var superdog = SuperDog("小黑",3,"wang",ActionFly())superdog.run()var superdog2 = SuperDog("小花",3,"wang",ActionMove())superdog2.run()
}

输出结果:

能飞就不要跑
狗在跑

其实这相当于是 动态地为类指定继承的接口的过程

在SuperDog类中,最后一个参数指定为ActionMethod类型,要传进一个接口的对象。刚好有两个类MethodFly和MethodMove实现了这两个接口,我们就可以传这两个对象进去,接着,SuperDog类继承了抽象类Dog和接口ActionMethod,但是这个接口不是由SuperDog自己实现的,它通过在后面加关键字by 并带上action,说明了接口是由参数中的action实现的。注意这个action必须要作为参数传进来才可以。在具体的调用中,我们需要传进两种实现了接口的类的对象,注意是对象,这里采用了匿名对象的方法,传入了构造函数。
因此,小花和小黑虽然都是超狗,但是因为其指定的接口代理不同,调用同名方法,实现的功能也可以是不同的。
可能有点绕,但是看懂了之后原理是很好理解的。

Kotlin学习(11)→抽象类、接口、接口代理相关推荐

  1. Kotlin - 面向对象之抽象类与接口

    接口 接口是一种约定或协议,需要使用 interface 定义: interface InputDevice {fun input(event: Any) } 接口不能有状态,我们可以在接口中声明一个 ...

  2. Java探索之旅(11)——抽象类与接口

    1.Java数据类型 ❶不可变类,是指当创建了这个类的实例后,就不允许修改它的属性值. 它包括: Primitive变量:boolean,byte, char, double ,float, inte ...

  3. Java学习:抽象类与接口

    抽象类 抽象类特点: A:抽象类和抽象方法必须用abstract关键字修饰 B:抽象类不一定有抽象方法,有抽象方法的类一定是抽象类 C:抽象类不能实例化 那么,如果实例化并使用呢? 按照多态的方式,由 ...

  4. Java学习之抽象类和接口

    什么是抽象类?抽象类的定义是这样的 Java语言中,用abstract 关键字来修饰一个类时,这个类叫作抽象类.抽象类是它的所有子类的公共属性的集合,是包含一个或多个抽象方法的类.抽象类可以看作是对类 ...

  5. Java 抽象类和接口内部类及综合

    11.抽象类和接口 1.在JDK1.7中,下述说法中抽象类与接口的区别与联系正确的有哪些? [多选题]全选 您的回答: A.抽象类中可以有普通成员变量,接口中没有普通成员变量. B.抽象类和接口中都可 ...

  6. Kotlin 学习笔记(八)—— Kotlin类与对象之接口

    Kotlin 学习笔记(八)-- Kotlin类与对象之接口 Kotlin学习笔记系列教程 Kotlin 学习笔记(一)-- 概述.学习曲线.开发工具.参考资料 Kotlin 学习笔记(二)-- 基础 ...

  7. 【Kotlin】Kotlin 抽象类与接口 ( 接口声明 | 接口实现 | 抽象类声明与实现 )

    文章目录 I . Kotlin 接口定义与实现 II . Kotlin 抽象类定义 III . Kotlin 类继承抽象类并实现接口 IV . Kotlin 接口与抽象类子类测试 I . Kotlin ...

  8. Kotlin学习之路(4)——类,对象和接口

    类,对象和接口 之前的篇章已经简单的介绍过如何声明一个类,也知道了一些简单的主构造方法来声明方法和属性,以及使用枚举类. 类和接口 接口 和Java相同的是,我们同样用interface来定义一个接口 ...

  9. 菜鸟学习笔记:Java基础篇5(抽象类与接口、回调函数、内部类)

    菜鸟学习笔记:Java面向对象篇下 抽象类 接口 回调函数 内部类 成员内部类 匿名内部类 抽象类 通过前面知识的学习,抽象类这个概念应该不难理解,但比较容易和后面要说的接口混淆,而且在面试中也比较爱 ...

最新文章

  1. android studio github 项目导入问题
  2. 为Chrome多账户添加单独的快捷方式
  3. golang变量使用细节
  4. [NOTE] sqli-labs Adv Injections
  5. amd cpu不能在cmd环境下运行java代码_00 开发环境搭建
  6. MySQL笔记-事务理论及并发存在的三个问题(脏读、不可重复读、幻读)演示
  7. mt4双线macd_【名师讲堂第三季】第六期:基于MACD指标的买卖策略精讲
  8. Python常见文件函数
  9. sqlalchemy与mysql映射
  10. javax maven项目缺少_教育平台项目后台管理系统:介绍与搭建
  11. 华中师范大学邮箱matlab,正版软件管理与服务平台(华中师范大学)
  12. 基于etcd+confd通过nginx对docker服务混合注册发现详解
  13. 推荐写代码的软件(IDE)——VS code的安装与使用,VS code中运行C语言、C++、Java、Python
  14. 加拿大签证材料(一家三口)
  15. 【场景化解决方案】OA审批与金智CRM数据同步
  16. php序列化 与json_PHP中serializen()与json_encode()的性能差异
  17. 稚晖君的HoloCubic 透明棱镜小电视
  18. IAR EW8051-8.1编译ZStack时,出现警告Warning[Pe069]: integer conversion resulted in truncation的解决办法
  19. 发送报警信息到微信公众号
  20. 什么是jsp,它有什么用+jsp的本质是什么

热门文章

  1. 干了两个星期,赚了3万!同城wifi贴项目揭秘!
  2. 电脑散热,夏天来了 电脑散热风扇太吵怎么办?
  3. 每周新品|鉴黄API上线 云市场2017最值得期待的产品盘点
  4. Kotlin基础(一)-- 搭建开发环境
  5. 一文彻底理解:训练集,验证集,测试集,交叉验证
  6. Java 集合深入理解(9):Queue 队列
  7. UR机械臂+RG2机械手控制 学习及实际控制 系列第三篇-1
  8. 图书租赁系统html,图书租赁管理系统
  9. 云桌面为什么凭这四点就能火
  10. java并发-JUC