接口和类方法中的 SELF
接口和类方法中的 SELF
由 王巍 (@ONEVCAT) 发布于 2015/06/10
我们在看一些接口的定义时,可能会注意到出现了首字母大写的 Self 出现在类型的位置上:
protocol IntervalType {
//...
/// Return `rhs` clamped to `self`. The bounds of the result, even
/// if it is empty, are always within the bounds of `self`
func clamp(intervalToClamp: Self) -> Self
//...
}
比如上面这个 IntervalType 的接口定义了一个方法,接受实现该接口的自身的类型,并返回一个同样的类型。
这么定义是因为接口其实本身是没有自己的上下文类型信息的,在声明接口的时候,我们并不知道最后究竟会是什么样的类型来实现这个接口,Swift 中也不能在接口中定义泛型进行限制。而在声明接口时,我们希望在接口中使用的类型就是实现这个接口本身的类型的话,就需要使用 Self 进行指代。
但是在这种情况下,Self 不仅指代的是实现该接口的类型本身,也包括了这个类型的子类。从概念上来说,Self 十分简单,但是实际实现一个这样的方法却稍微要转个弯。为了说明这个问题,我们假设要实现一个 Copyable 的接口,满足这个接口的类型需要返回一个和接受方法调用的实例相同的拷贝。一开始我们可能考虑的接口是这样的:
protocol Copyable {
func copy() -> Self
}
这是很直接明了的,它应该做的是创建一个和接受这个方法的对象同样的东西,然后将其返回,返回的类型不应该发生改变,所以写为 Self。然后开始尝试实现一个 MyClass 来满足这个接口:
class MyClass: Copyable {
var num = 1
func copy() -> Self {
// TODO: 返回什么?
// return
}
}
我们一开始的时候可能回写类似这样的代码:
func copy() -> Self {
let result = MyClass()
result.num = num
return result
}
但是显然类型是有问题的,因为该方法要求返回一个抽象的、表示当前类型的 Self,但是我们却返回了它的真实类型 MyClass,这导致了无法编译。也许你会尝试把方法声明中的 Self 改为 MyClass,这样声明就和实际返回一致了,但是很快你会发现这样的话,实现的方法又和接口中的定义不一样了,依然不能编译。
为了解决这个问题,我们在这里需要的是通过一个和当前上下文 (也就是和 MyClass) 无关的,又能够指代当前类型的方式进行初始化。希望你还能记得我们在对象类型中所提到的 dynamicType,这里我们就可以使用它来做初始化,以保证方法与当前类型上下文无关,这样不论是 MyClass 还是它的子类,都可以正确地返回合适的类型满足 Self 的要求:
func copy() -> Self {
let result = self.dynamicType()
result.num = num
return result
}
但是很不幸,单单是这样还是无法通过编译,编译器提示我们如果想要构建一个 Self 类型的对象的话,需要有 required 关键字修饰的初始化方法,这是因为 Swift 必须保证当前类和其子类都能响应这个 init 方法。在这个例子中,我们添加上一个 required 的 init 就行了。最后,MyClass 类型是这样的:
class MyClass: Copyable {
var num = 1
func copy() -> Self {
let result = self.dynamicType()
result.num = num
return result
}
required init() {
}
}
我们可以通过测试来验证一下行为的正确性:
let object = MyClass()
object.num = 100
let newObject = object.copy()
object.num = 1
println(object.num) // 1
println(newObject.num) // 100
而对于 MyClass 的子类,copy() 方法也能正确地返回子类的经过拷贝的对象了。
另一个可以使用 Self 的地方是在类方法中,使用起来也十分相似,核心就在于保证子类也能返回恰当的类型。
接口和类方法中的 SELF的更多相关文章
- Delphi接口的底层实现(接口在内存中仍然有其布局,它依附在对象的内存空间中,有汇编解释)——接口的内存结构图,简单清楚,深刻 good
引言 接口是面向对象程序语言中一个很重要的元素,它被描述为一组服务的集合,对于客户端来说,我们关心的只是提供的服务,而不必关心服务是如何实现的:对于服务端的类来说,如果它想实现某种服务,实现与该服务相 ...
- Java 私有接口 【类中嵌套接口】
1.前言 接口十分常用,能规范实现类的命名 和 实现多个实现类的向上转型成统一类型 ,但是接口的修饰符只能是 public吗? 当然不是,可以是private , 难道是像这样? 显然不可以,已经报错 ...
- Java中接口和Sala中的特质的区别?
1.先要区分是Java中哪个版本的接口,因为Java中不同版本接口是不一样2.Java8之前的接口(不包含Java8),这个版本的接口只能属性和抽象方法,和Scala中的特质有完全的不用因为Scala ...
- 老猿学5G扫盲贴:中移动的5G计费架构中Nchf'服务化接口以及CHF中的AGF
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 一.关于Nchf' 在中移动企标中出现了在3GPP ...
- C# 用SoapUI调试WCF服务接口(WCF中包含用户名密码的验证)
问题描述: 一般调试wcf程序可以直接建一个单元测试,直接调接口. 但是,这次,我还要测试在接口内的代码中看接收到的用户名密码是否正确,所以,单一的直接调用接口方法行不通, 然后就想办法通过soapU ...
- 对接第三方支付接口-获取http中的返回参数
这几天对接第三方支付接口,在回调通知里获取返回参数,有一家返回的json格式,请求参数可以从标准输入流中获取. //1.解析参数 , 读取请求内容 BufferedReader br; String ...
- SpringMVC框架下实现JSON(类方法中回传数据到jsp页面,使用jQuery方法回传)
JSON的实现,即将需要的数据回传到jsp页面: 1>.加入实现Json的三个架包到lib中:2>.目标方法上边加入注解,需要返回的值3>.在jsp页面中书写jQuery方法: ec ...
- IOS创建目录接口createDirectoryAtPath:withIntermediateDirectories:中参数attributes的设置
在应用程序执行时,经常需要本地化保存一些重要的数据,这时就有可能需要创建一些目录.Objective-C提供了一个非常强大的创建目录的接口: - (BOOL)createDirectoryAtPath ...
- python编程 之 PyMysql包接口,python中如何使用数据库
1,环境介绍 要求:使用数据库TESTDB.EMPLOYMENT EMPLOYEE表字段为 FIRST_NAME, LAST_NAME, AGE, SEX 和 INCOME. 2,基本用法: impo ...
随机推荐
- bzoj 4032: [HEOI2015]最短不公共子串【dp+SAM】
第一.二问: 就是最小的最长公共长度+1,设f[i][j]为a匹配到i,b匹配到j,第一问的转移是f[i][j]=(a[i]==b[j]?f[i-1][j-1]+1:0),第二问的转移是f[i][j] ...
- Ruby对象模型总结
参考<Ruby元编程>,元编程,即 用来编写代码的代码 . 对象由一组实例变量和一个类的引用组成 对象的方法存在与对象所属的类中,类似js中的prototype,在ruby中准确的说,应该 ...
- 毕业N年后,请不要像我一样被档案烦死
目录 一. 提醒大学生:深刻重视档案,避免以后麻烦! 二.说说我因为档案造成的烦心事! 三.说说档案这档子事: 四.档案如此重要,为什么有些人却成了弃档族? 五.档案该怎么操作才能不当"弃档 ...
- hyperledger fabric 1.0.5 分布式部署 (二)
环境:2台 ubuntu 16.04 角色列表 角色 IP地址 宿主端口 docker端口 peer0.org1.example.com 47.93.249.250 7051 7051 pe ...
- 选择提示框UIAlertController 和网络状态判断AFNetworking
// 选择提示框 DownloadView *vc = [[DownloadView alloc] initWithFrame:CGRectMake(, , SCREEN_WIDTH, SCREEN_ ...
- HDU 1233(最小生成树)
HDU 1233(最小生成树 模板) #include <iostream> #include <algorithm> #include <cstdio> usin ...
- __enter__,__exit__上下文管理协议
上下文管理协议__enter__,__exit__ 用途或者说好处: 1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预 2.在需要管理一些资源比 ...
- BIO,NIO,AIO总结
熟练掌握 BIO,NIO,AIO 的基本概念以及一些常见问题是你准备面试的过程中不可或缺的一部分,另外这些知识点也是你学习 Netty 的基础. BIO,NIO,AIO 总结 1. BIO (Bloc ...
- asp.net调试技巧
一眨眼的功夫,自己已经学习asp.net的有一年的功夫了.虽然称不上什么大神,但是也有一点知识的积累.就写一片调试的入门文章给那些刚刚入门迷茫的童鞋们.希望你学习了我这篇文章能从迷茫的生活中找回编程的 ...
- MVC验证注解(不包含自定义验证)