Kotlin快速入门系列6

Kotlin的接口与扩展

接口

与Java类似,Kotlin使用interface关键字定义接口,同时允许方法有默认实现:

interface KtInterfaceTest {fun method()fun methodGo(){println("上面方法未实现,此方法已实现")}
}

接口实现

一个类或者对象可以实现一个或多个接口。

class Demo : KtInterfaceTest{override fun method() {println("在类Demo中实现KtInterfaceTest接口的method()")}
}

我们在主函数中调用一下:

fun main(args: Array<String>) {val demo =  Demo()demo.method()demo.methodGo()
}

对应控制台输出为:

接口中的属性

接口中的属性只能是抽象的,不允许初始化值,接口不会保存属性值。在实现接口时,必须重写属性。

interface KtInterfaceTest {var urlString : String   //抽象属性,不允许实例化具体值
}class Demo : KtInterfaceTest{override var urlString: String = "www.google.com"
}

函数重写冲突

接口的重写跟类的重写类似,如果父类中声明了许多类型,有可能出现一个方法的多种实现,则必须重写这个成员并且提供自己的实现,而要使用父类中提供的方法,则需要用super<Base>来表示。

interface A {fun foo() { print("A") }fun bar()
}interface B {fun foo() { print("B") }fun bar() { print("bar") }
}class C : A {override fun bar() { print("bar") }
}class D : A, B {override fun foo() {super<A>.foo()super<B>.foo()}
}

如上代码,接口A,B都声明了foo()、bar()函数,接口B两个方法都有实现,接口A只实现了foo(),而bar()在A接口中并没有声明是抽象函数,所以C类要重写并实现bar()。而D类,同时继承A,B两个接口,不用重写bar()方法,因为继承的B接口已经实现同名方法。但由于继承了两个接口的foo()实现,所以需要用super<base类>关键字来区分。

扩展

kotlin同C#和Gson类似,能够扩展一个类的新的属性或函数(方法)而无需继承该类,且无需使用装饰模式在内的任何类型的设计模式。扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。

扩展函数

扩展函数可以在已有类中添加新的方法,不会对原类做修改,扩展函数的定义形式:

fun receiverType.functionName(params){body
}

· receiverType:表示函数扩展的对象(接收者)

· functionName:扩展函数的名称

· params:扩展函数的参数,可以为null

下面是一个对User类的扩展:

class User(var number:Int)fun User.Printf(){println(" 用户号 : $number")
}fun main(args: Array<String>) {var user = User(123)user.Printf()
}

对应输出结果为:

同时,这个扩展函数还可改为:

fun User.Printf(){println(" 用户号 : "+this.number)
}

运行结果也是一致的。这里要说明下,这里的this关键字指代的是函数扩展对象(receiver object)(也就是调用扩展函数时, 在点号之前指定的对象实例)。

扩展是静态解析的

扩展不能真正的修改他们的(扩展)类。扩展方法并没有在类中插入新的成员,仅仅是可以通过该类型的变量使用点的表达式调用这个函数。

扩展函数一直强调是静态解析的,并不是接受者类型的虚拟成员。在调用扩展函数的时候,具体被调用的是哪一个函数,这一点由调用函数的对象表达式来决定,而不是动态的类型来决定的,如下示例:

open class Fruitclass Apple: Fruit()fun Apple.foo() = " is apple "   // 扩展函数 foofun Fruit.foo() = " is fruit"   // 扩展函数 foofun printFoo(f: Fruit) {println(f.foo())  // 类型是 Fruit 类
}fun main(arg:Array<String>){printFoo(Apple())
}

对应的输出结果是:

若扩展函数和成员函数一致,则使用该函数时,会优先使用成员函数。

class Apple{fun foo(){println(" is apple in class ")   // 成员函数 foo}
}fun Apple.foo() = println(" is apple in extend ")   // 扩展函数 foofun main(arg:Array<String>){var apple = Apple()apple.foo()
}

对应的控制台输出结果为:

扩展一个空对象

在扩展函数内,可通过this关键字来判断接收者(receiverType)是否为null。这样,即使接收者为null,也可以调用扩展函数。如下:

fun Any?.toString(): String {if (this == null) return "null"// 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()解析为 Any 类的函数toString()return toString()
}fun main(arg:Array<String>){var demo = nullprintln(demo.toString())
}

对应的控制台输出结果为:

扩展属性

除了扩展函数,kotlin也支持扩展属性。扩展属性允许定义在类或者kotlin文件中,不允许定义在函数中。初始化属性因为属性没有后端字段(backing field),所以不允许被初始化,只能由显式提供的 getter/setter 定义。

val User.foo = 1 // 错误:扩展属性不能有初始化器val <T> List<T>.lastIndex: Intget() = size - 1              //正确

值得注意的是,扩展属性只能被声明为 val。

伴生对象的扩展

如果一个类有一个伴生对象 ,你也可以为伴生对象定义扩展函数和属性。伴生对象通过"类名."形式调用伴生对象,伴生对象声明的扩展函数,通过用类名限定符来调用:

class NormalClass {companion object { }  // 将被称为 "Companion"
}fun NormalClass.Companion.foo() {println(" 伴生对象Companion的扩展函数 ")
}val NormalClass.Companion.no: Intget() = 10fun main(args: Array<String>) {println(" no:${NormalClass.no} ")NormalClass.foo()
}

在控制台对应的输出结果为:

补充:伴生对象内的成员相当于 Java 中的静态成员,其生命周期伴随类始终,在伴生对象内部可以定义变量和函数,这些变量和函数可以直接用类名引用。

对于伴生对象扩展函数,有两种形式,一种是在类内扩展,一种是在类外扩展,这两种形式扩展后的函数互不影响(甚至名称都可以相同),即使名称相同,它们也完全是两个不同的函数,并且有以下特点:

 (1)类内扩展的伴随对象函数和类外扩展的伴随对象可以同名,它们是两个独立的函数,互不影响;

 (2)当类内扩展的伴随对象函数和类外扩展的伴随对象同名时,类内的其它函数优先引用类内扩展的伴随对象函数,即对于类内其它成员函数来说,类内扩展屏蔽类外扩展;

 (3)类内扩展的伴随对象函数只能被类内的函数引用,不能被类外的函数和伴随对象内的函数引用;

 (4)类外扩展的伴随对象函数可以被伴随对象内的函数引用。

示例代码如下:

class RunProgram {companion object {val mFieldNum: Int = 1var mFieldString = "this is mFieldString"fun companionFun1() {println("this is 1st companion function.")foo()}fun companionFun2() {println("this is 2st companion function.")companionFun1()}}fun RunProgram.Companion.foo() {println("伴随对象的扩展函数(内部)")}fun test2() {RunProgram.foo()}init {test2()}
}
val RunProgram.Companion.no: Intget() = 10
fun RunProgram.Companion.foo() {println("foo 伴随对象外部扩展函数")
}
fun main(args: Array<String>) {println("no:${RunProgram.no}")println("field1:${RunProgram.mFieldNum}")println("field2:${RunProgram.mFieldString}")RunProgram.foo()RunProgram.companionFun2()
}

对应控制台输出结果为:

扩展的作用域

通常扩展函数或扩展属性就定义在顶级包下:

package ktfoo.programfun Ktz.goo() { …… }

如果要使用所定义包之外的一个扩展, 可通过import导入扩展的函数名进行使用:

package com.example.demoimport ktfoo.program.goo // 导入所有名为 goo 的扩展
// 或者
import ktfoo.program.*   // 从 ktfoo.program 导入一切fun usage(baz: Ktz) {baz.goo()
}

扩展声明为成员

在kotlin中,一个类内部你可以为另一个类声明扩展。在这个扩展中,有个多个隐含的接受者,其中扩展方法定义所在类的实例称为分发接受者,而扩展方法的目标类型的实例称为扩展接受者

class Car {fun bar() { println("Car bar") }
}class Plane {fun baz() { println("Plane baz") }fun Car.foo() {bar()   // 调用 Car.barbaz()   // 调用 Plane.baz}fun caller(car: Car) {car.foo()   // 调用扩展函数}
}fun main(args: Array<String>) {val Plane: Plane = Plane()val car: Car = Car()Plane.caller(car)
}

可以推算出控制台输出为:

在上段代码中, Plane 类内,创建了Car类的扩展。此时,Plane被成为分发接受者,而Car为扩展接受者。从上例中,可以清楚的看到,在扩展函数中,可以调用派发接收者的成员函数。

假如在调用某一个函数,而该函数在分发接受者和扩展接受者均存在,则以扩展接收者优先,要引用分发接收者的成员你可以使用限定的 this 语法。示例如下:

class Car {fun bar() { println("Car bar") }
}class Plane {fun bar() { println("Plane bar") }  //注意fun Car.foo() {bar()   // 调用 Car.barthis@Plane.bar()   // 调用 Plane.baz}fun caller(car: Car) {car.foo()   // 调用扩展函数}
}fun main(args: Array<String>) {val Plane: Plane = Plane()val car: Car = Car()Plane.caller(car)
}

对应的控制台输出:

值得一提的是,以成员的形式定义的扩展函数, 可以声明为 open , 而且可以在子类中覆盖。(也可以说,在这类扩展函数的派发过程中, 针对分发接受者是虚拟的(virtual), 但针对扩展接受者仍然是静态的。)

open class Car {
}class SUV : Car(){}open class Plane {open fun Car.drive() {println(" Car driving in the Plane ")}open fun SUV.drive() {println(" SUV driving in the Plane ")}fun caller(car: Car){car.drive()    //调用扩展函数}
}class FighterPlane : Plane(){override fun Car.drive() {println(" Car driving in the FighterPlane ")}override fun SUV.drive() {println(" SUV driving in the FighterPlane ")}
}fun main(args: Array<String>) {Plane().caller(Car())FighterPlane().caller(Car())        //分发接收者虚拟解析Plane().caller(SUV())               //扩展接收者静态解析
}

对应控制台输出为:

End,如有问题请留言讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/656560.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Codeforces Round 898 (Div. 4)

目录 A. Short Sort B. Good Kid C. Target Practice D. 1D Eraser E. Building an Aquarium F. Money Trees G. ABBC or BACB H. Mad City A. Short Sort 这种判断是否能变成目标串的我们都是通过一定手段然后看最后是否是直接变成目标串即可 void solve(){string s; …

JS第一课简单看看这是啥东西

1.什么是JavaScript JS是一门编程语言&#xff0c;是一种运行在客户端(浏览器)的编程语言&#xff0c;主要是让前端的画面动起来&#xff0c;注意HTML和CSS不是编程语言&#xff0c;他俩是一种标记语言。JS只要有浏览器就能运行不用跟Python或者Java一样上来装一个jdk或者Pyth…

12306提示人证核验失败问题解决方案

问题环境&#xff1a;手机已经 Root 并且安装了其他软件 认证时提示 官方客服回复: 可能是我的人脸发生了太大变化导致&#xff0c;建议我去身份证的公安部门更新人脸信息&#xff0c;但是想一想又不对&#xff0c;如果发生了大变化所有 App 使用的都是统一的公安部的人脸信息…

探索数字经济:从基础到前沿的奇妙旅程

新一轮技术革命方兴未艾&#xff0c;特别是以人工智能、大数据、物联网等为代表的数字技术革命&#xff0c;催生了一系列新技术、新产业、新模式&#xff0c;深刻改变着世界经济面貌。数字经济已成为重组全球要素资源、重塑全球经济结构、改变全球竞争格局的关键力量。预估到20…

关于maven项目构建的解释

在Idea中使用模块化构建项目 项目介绍&#xff1a; sky-server依赖sky-pojo和sky-common&#xff0c;继承sky-take-outsky-pojo继承sky-take-outsky-common继承sky-take-out 由于Idea编译器自动识别引入的模块&#xff0c;所以在Idea中可以运行项目。 在Idea中使用maven打包…

Redis -- 背景知识

目录 特性 为啥Redis快? 应用场景 Redis不能做什么&#xff1f; Redis是在内存中存储数据的一个中间件&#xff0c;用作为数据库&#xff0c;也可以用作为缓存&#xff0c;在分布式中有很高的威望。 特性 In-memory data structures&#xff1a;在内存中存储数据key-val…

设计模式——职责链模式(Chain of Responsibility Pattern)

概述 职责链模式(Chain of Responsibility Pattern)&#xff1a;避免请求发送者与接收者耦合在一起&#xff0c;让多个对象都有可能接收请求&#xff0c;将这些对象连接成一条链&#xff0c;并且沿着这条链传递请求&#xff0c;直到有对象处理它为止。职责链模式是一种对象行为…

少儿编程 中国电子学会图形化编程2021年3月等级考试Scratch三级真题解析(选择题、判断题)

1.在《采矿》游戏中&#xff0c;当角色捡到黄金时财富值加1分&#xff0c;捡到钻石时财富值加2分&#xff0c;下面哪个程序实现这个功能&#xff1f; A&#xff1a; B&#xff1a; C&#xff1a; D&#xff1a; 2.设计一个和在20以内&#xff08;包括20&#xff09;的整数加法…

通过docker构建基于LNMP的WordPress项目

计划通过自定义网络模式&#xff0c;创建一个172.18.0.0/16网段 nginx&#xff1a;172.18.0.2:80 php&#xff1a;172.18.0.3:9000 mysql&#xff1a;172.18.0.4:3306 创建nginx的镜像 准备好nginx的安装包 准备nginx的网页目录和wordpress网站目录以及nginx.conf文件 编…

前段,关于Javascript的学习,数据类型

<html> <head> <title>Javascript Traning</title> <script> //注意Javascript里边的变量类型都用 var开头 var value "abc";//字符串类型 alert(value); var value110; var value210;//数字类型 alert(value2); //数字相加 aler…

中国地区cetos7.9 install kubeadmin

第 1 步&#xff1a;禁用 SELinux&#xff08;可选但推荐&#xff09; 如何在 CentOS 7 上查找 SELinux 状态 sestatus另一种选择是运行以下 cat 命令&#xff1a; vi /etc/selinux/config SELINUXdisabled rebootcentos7 linux 安装k8s前下面操作的作用是&#xff1f; cat…

用Python库pillow处理图像

入门知识 颜色。如果你有使用颜料画画的经历&#xff0c;那么一定知道混合红、黄、蓝三种颜料可以得到其他的颜色&#xff0c;事实上这三种颜色就是美术中的三原色&#xff0c;它们是不能再分解的基本颜色。在计算机中&#xff0c;我们可以将红、绿、蓝三种色光以不同的比例叠加…

腾讯云云监控实践:使用云审计 CloudAudit SDK 精准管理腾讯云资源

文章目录 一、什么是腾讯云的操作审计 CloudAudit二、CloudAudit 有哪些优势三、CloudAudit 应用场景举例3.1 安全分析3.2 资源变更跟踪3.3 合规性审计 四、使用云审计 SDK 进行云监控4.1 安装环境包 PHP4.2 下载并解压云审计 PHP SDK4.3 创建的腾讯云持久证书&#xff08;如果…

【添加公众号】CSDN官方指定推广功能

一、场景 二、说明 三、要求&#xff08;其中之一&#xff09; 三、实战 Stage 1&#xff1a;进入推广管理 Stage 2&#xff1a;申请推广 1、微信公众号推广 2、微信号推广 Stage 3&#xff1a;提交审核 Stage 4&#xff1a;查看结果 Stage 5&#xff1a;开启推广 S…

2024年Java基础面试题

2024年Java实战面试题&#xff08;北京&#xff09;_java 5 年 面试-CSDN博客 一、redis基础类型&#xff1a;string&#xff08;字符串&#xff09;、hash&#xff08;哈希&#xff09;、list&#xff08;列表&#xff09;、set&#xff08;集合&#xff09;、sort set &#…

深度学习与神经网络pytorch版 2.3 线性代数

深度学习与神经网络pytorch版 2.3 线性代数 目录 深度学习与神经网络pytorch版 2.3 线性代数 1. 简介 2. 线性代数 2.3.1 标量 ​编辑2.3.2 向量 2.3.3 矩阵 2.3.4 张量及其性质 2.3.5 降维 2.3.6 非降维求和 2.3.7 点积 2.3.8 矩阵-向量积 2.3.9 矩阵-矩阵乘法 …

linux平台 LED情报板网关项目总结

LED情报板目前已经基本稳定 主要存在两个版本LED1.5 和LED2 两个版本的主要差别是 &#xff1a; 1.1.5的配置文件存在本地 2.0的存在平台 需要通过接口从平台获取 2.1.5的下发消息的时候需要同步返回 2.0的是异步返回&#xff0c;所以1.5的要尽可能完成 不然会导致发 布平台…

【python】求解线性方程

唯一解(矩阵法、solve函数) { 3 x y 9 x 2 y 8 \left\{ \begin{matrix} 3xy9 \\ x2y8 \end{matrix} \right. {3xy9x2y8​ A x b Axb Axb,其中 A [ 3 1 1 2 ] A\left[\begin{matrix} 3 & 1 \\ 1 &2 \end{matrix}\right] A[31​12​], B [ 9 8 ] B\left[\begin{m…

境外投资企业备案结果公开名录列表数据

境外投资企业备案结果公开名录列表数据 1、时间&#xff1a;更新至2023年10月16日 2、指标&#xff1a;境外投资企业_机构、境内投资者名称、投资国别地区 3、来源&#xff1a;商务部 4、指标解释 境外投资企业&#xff08;机构&#xff09;备案结果公开名录列表&#xff…

Java使用io流生成pdf文件

首先生成pdf和正常请求接口一样&#xff0c;直接写~ Controller层&#xff1a; 第一个注解&#xff1a;最顶层增加 Controller 注解&#xff08;控制器&#xff09;不多讲了 直接加上。 第二个注解&#xff1a;最顶层增加 CrossOrigin 注解此注解是为了浏览器请求的时候防…