1,扩展概念(OC中早期就有此功能)

  Kotlin 能够扩展一个类的新功能而无需继承该类或者使用像装饰者这样的设计模式。这通过叫做 扩展 的特殊声明完成。例如,你可以为一个你不能修改的、来自第三方库中的类编写一个新的函数。这个新 增的函数就像那个原始类本来就有的函数一样,可以用普通的方法调用。这种机制称为扩展函数此外,也有扩展属性, 允许你为一个已经存在的类添加新的属性

2.扩展函数,声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀。下面代码为 MutableList<Int> 添加一个 swap 函数:

fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // “this”对应该列表
this[index1] = this[index2]
this[index2] = tmp
} //这个 this 关键字在扩展函数内部对应到接收者对象(传过来的在点符号前的对象)现在,我们对任意 MutableList<Int> 调用该函数了:
val list = mutableListOf(1, 2, 3)
list.swap(0, 2) // “swap()”内部的“this”会保存“list”的值 //当然,这个函数对任何 MutableList<T> 起作用,我们可以泛化它:
//为了在接收者类型表达式中使用泛型,我们要在函数名前声明泛型参数
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // “this”对应该列表
this[index1] = this[index2]
this[index2] = tmp
}

3.扩展是静态解析的

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

我们想强调的是扩展函数是静态分发的,即他们不是根据接收者类型的虚方法。这意味着调用的扩展函 数是由函数调用所在的表达式的类型来决定的,而不是由表达式运行时求值结果决定的。例如

open class Shape
class Rectangle : Shape() fun Shape.getName() = "Shape"
fun Rectangle.getName() = "Rectangle"
fun printClassName(s: Shape) {
println(s.getName())
}
printClassName(Rectangle())
//这个例子会输出 "Shape",因为调用的扩展函数只取决于参数 s 的声明类型,该类型是 Shape 类 //如果一个类定义有一个成员函数与一个扩展函数,而这两个函数又有相同的接收者类型、相同的名字,并且都适用给定的参数,这种情况总是取成员函数。例如:
class Example {
fun printFunctionType() {
println("Class method")
}
} fun Example.printFunctionType() {
println("Extension function")
}
Example().printFunctionType()
//这段代码输出“Class method”。 //当然,扩展函数重载同样名字但不同签名成员函数也完全可以
class Example {
fun printFunctionType() {
println("Class method")
}
} fun Example.printFunctionType(i: Int) {
println("Extension function")
}
Example().printFunctionType(1)

4.可空接收者

  注意可以为可空的接收者类型定义扩展。这样的扩展可以在对象变量上调用,即使其值为 null,并且可 以在函数体内检测 this == null ,这能让你在没有检测 null 的时候调用 Kotlin 中的toString():检测发生在扩展函数的内部。

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

5.扩展属性,与函数类似,Kotlin 支持扩展属性

val <T> List<T>.lastIndex: Int
get() = size - 1

  5.1 注意:由于扩展没有实际的将成员插入类中,因此对扩展属性来说幕后字段是无效的。这就是为什么扩 展属性不能有初始化器。他们的行为只能由显式提供的 getters/setters 定义

val House.number = 1 // 错误:扩展属性不能有初始化器

6.伴生对象的扩展,如果一个类定义有一个伴生对象 ,你也可以为伴生对象定义扩展函数与属性。就像伴生对象的常规成员 一样,可以只使用类名作为限定符来调用伴生对象的扩展成员:

class MyClass {
companion object {} // 将被称为 "Companion"
} fun MyClass.Companion.printCompanion() {
println("companion")
} fun main() {
MyClass.printCompanion()
}

7.扩展的作用域

//大多数时候我们在顶层定义扩展⸺直接在包里
package org.example.declarations
fun List<String>.getLongestString() { /*......*/}

7.1 要使用所定义包之外的一个扩展,我们需要在调用方导入它

package org.example.usage
import org.example.declarations.getLongestString fun main() {
val list = listOf("red", "green", "blue")
list.getLongestString ()
}

8.扩展声明为成员

  8.1 在一个类内部你可以为另一个类声明扩展。在这样的扩展内部,有多个 隐式接收者——其中的对象成员 可以无需通过限定符访问。扩展声明所在的类的实例称为 分发接收者,扩展方法调用所在的接收者类型 的实例称为 扩展接收者。

class Host(val hostname: String) {
fun printHostname() {
print(hostname)
}
} class Connection(val host: Host, val port: Int) {
fun printPort() {
print(port)
} fun Host.printConnectionString() {
printHostname() // 调用 Host.printHostname() print(":")
printPort() // 调用 Connection.printPort()
} fun connect() { /*......*/
host.printConnectionString()
}
} fun main() {
Connection(Host("kotl.in"), 443).connect()
//Host("kotl.in").printConnectionString(443) // 错误,该扩展函数在 Connection 外不可用
}

  8.2 对于分发接收者与扩展接收者的成员名字冲突的情况,扩展接收者优先。要引用分发接收者的成员你可 以使用 限定的 this 语法。

class Connection {
fun Host.getConnectionString() {
toString() // 调用 Host.toString()
this@Connection.toString() // 调用 Connection.toString() }
}
}

  8.3 声明为成员的扩展可以声明为 open 并在子类中覆盖。这意味着这些函数的分发对于分发接收者类型 是虚拟的,但对于扩展接收者类型是静态的

open class Base {}
class Derived : Base() {}
open class BaseCaller {
open fun Base.printFunctionInfo() {
println("Base extension function in BaseCaller")
} open fun Derived.printFunctionInfo() {
println("Derived extension function in BaseCaller")
} fun call(b: Base) {
b.printFunctionInfo() // 调用扩展函数
}
} class DerivedCaller : BaseCaller() {
override fun Base.printFunctionInfo() {
println("Base extension function in DerivedCaller")
} override fun Derived.printFunctionInfo() {
println("Derived extension function in DerivedCaller")
}
} fun main() {
BaseCaller().call(Base()) // “Base extension function in BaseCaller”
DerivedCaller().call(Base()) // “Base extension function in DerivedCaller”——分发接收者虚拟解析
DerivedCaller().call(Derived()) // “Base extension function in DerivedCaller”——扩展接收者静态解析
}

9.关于可见行的说明

扩展的可⻅性与相同作用域内声明的其他实体的可⻅性相同。例如:
— 在文件顶层声明的扩展可以访问同一文件中的其他 private 顶层声明;
— 如果扩展是在其接收者类型外部声明的,那么该扩展不能访问接收者的 private 成员

kotlin类与对象——>扩展的更多相关文章

  1. Kotlin 类和对象

    类定义 Kotlin 类可以包含:构造函数和初始化代码块.函数.属性.内部类.对象声明. Kotlin 中使用关键字 class 声明类,后面紧跟类名: class Runoob { // 类名为 R ...

  2. python类和对象-扩展

    1.为类或对象动态创建属性或方法 2.__slots__ = ('name','age'),[实例]只能创建指定的属性属性或方法 ---新式类 3.执行父类的构造函数的不同Father.__init_ ...

  3. Kotlin语法(类和对象)

    二.类和对象: 1. 类定义: 类的声明包含类名,类头(指定类型参数,主构造函数等等),以及类主体,用大括号包裹.类头和类体是可选的:如果没有类体可以省略大括号. class Invoice{ } 2 ...

  4. 扩展javascript扩展(类,对象,原型)

     扩展javascript扩展(类,对象,原型)

  5. Kotlin基础(三)类、对象和接口

    类.对象和接口 一.定义类的继承结构 一)Kotlin中的接口 Kotlin的接口与Java8中相似,它们可以包含抽象方法的定义以及非抽象方法的实现,但它们不能包含任何状态. interface Cl ...

  6. ES6...扩展运算符(数组或类数组对象)

    数组和类数组对象定义 数组:[] 类数组对象:只包含使用从零开始,且自然递增的整数做键名,并且定义了length表示元素个数的对象,我们就认为他是类数组对象. 数组使用 let foo_arr = [ ...

  7. atitit.编程语言 类与对象的 扩展机制.doc

    atitit.编程语言 类与对象的 扩展机制.doc 1.1. Java 下一代: 没有继承性的扩展1 1.2. 继承1 1.3. 使用cglib动态为Java类添加方法1 1.4. 工具类 1 1. ...

  8. Kotlin类:功能更强、而更简洁(KAD 03)

    作者:Antonio Leiva 时间:Dec 7, 2016 原文链接:http://antonioleiva.com/classes-kotlin/ Kotlin类尽可能简单,这样用较少的代码完成 ...

  9. Objective-C Runtime 运行时之一:类与对象

    Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理.这种动态语言的优势在于:我们写代码时更具灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一 ...

  10. Scala 编程---类和对象

    类是对象的蓝图.一旦你定义了类,你就可以用关键字new从类的蓝图里创建对象.比方说,如果给出了类的定义: class ChecksumAccumulator { // class definition ...

随机推荐

  1. 【RabbitMQ】05 通配符模式

    需要设定交换机模式为通配符模式 Topic 在绑定规则上采用通配描述实现动态绑定 创建通配符模式的生产者 package cn.dzz.topicQueue; import com.rabbitmq. ...

  2. AI的发展需要有应用和落地场景 —— 李开复:传统公司看不懂技术,大模型落地B端阻碍多

    引自:https://baijiahao.baidu.com/s?id=1801826206644007472&wfr=spider&for=pc "我们投了七八家机器人企业 ...

  3. signal-slot:python版本的多进程通信的信号与槽机制(编程模式)的库(library) —— 强化学习ppo算法库sample-factory的多进程包装器,实现类似Qt的多进程编程模式(信号与槽机制) —— python3.12版本下成功通过测试

    什么是 Qt signal-slot库项目地址: https://github.com/alex-petrenko/signal-slot 该库实现的主要原理: 要注意这个项目的library只是对原 ...

  4. 【转载】 Ring Allreduce (深度神经网络的分布式计算范式 -------------- 环形全局规约)

    作者:初七123链接:https://www.jianshu.com/p/8c0e7edbefb9来源:简书著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. ----------- ...

  5. docker 容器(container)使用ssh服务登录一段时间无操作后自动断开问题解决

    如题,ssh登录建立好的docker容器,无操作一段时间后,发现ssh自动断开: 解决方法: 修改配置文件 /ect/ssh/sshd_config 在文件最后添加下面内容: PermitRootLo ...

  6. Lazysysadmin靶机笔记

    Lazysysadmin靶机笔记 概述 lazysysadmin是一台Vulnhub靶机,整体比较简单. 靶机地址:https://pan.baidu.com/s/19nBjhMpGkdBDBFSnM ...

  7. Ubuntu搭建ssh服务器

    Ubuntu安装后默认只有ssh客户端,即只能在Ubuntu内去连接其他ssh服务器,若想实现我们的Ubuntu被其他主机远程连接,则需要自己去安装ssh服务端. 安装 进入Ubuntu18.04,打 ...

  8. 从海量信息中脱颖而出:Workflow智能分析解决方案,大语言模型为AI科技文章打造精准摘要评分体系(总篇章)

    从海量信息中脱颖而出:Workflow智能分析解决方案,大语言模型为AI科技文章打造精准摘要评分体系(总篇章) 1.简介 该项目整合了编程.AI.产品设计.商业科技及个人成长等多领域的精华内容,源自顶 ...

  9. CMake构建学习笔记6-giflib库的构建

    前面构建的zlib.libpng.libjpeg和libtiff都提供了CMakeList.txt文件,因此都可以通过CMake进行构建.不过有的依赖库是并没有CMakeList.txt文件,也就是官 ...

  10. Devexpress GridView使用技巧

    1.表格数据根据前面列的值展示不同的值 例子:根据检测类型(定量.定性)展示,定性展示合格与不合格,定量展示实际值 实现如下: 1.使用 表格CustomColumnDisplayText事件 //需 ...