Type Erasure Pattern

We can use the type erasure pattern to combine both generic type parameters and associated types to satisfy both the compiler and our flexibility goals.

We will need to create three discrete types in order to satisfy these constraints. This pattern includes an abstract base class, a private box class, and finally a public wrapper class. This same pattern, as diagramed below, is even used in the Swift standard library.

Abstract Base

The first step of the process is to create an abstract base class. This base class will have three requirements:

  1. Conform to the Row protocol.
  2. Define a generic type parameter Model that serves as Row’s associated type.
  3. Be abstract. (each function must be overridden by a subclass)

This class will not be used directly, but instead subclassed in order to bind the generic type constraint to our protocol’s associated type. We’ve marked this class private as well as by convention prefixing it with an _.

 

Private Box

Next we create a private Box class with these requirements:

  1. Inherit from the generic base class _AnyRowBase.
  2. Define a generic type parameter Concrete that itself conforms to Row.
  3. Store an instance of Concrete for later usage.
  4. Trampoline each Row protocol function calls to the stored Concrete instance.

This class is referred to as a Box class, because it holds a reference to our concrete implementer of the protocol. In our case this would be a FileCellFolderCell or DetailFileCell all of which implement Row. We receive the Row protocol conformance from our super class _AnyRowBase and override each function by trampolining the call over to the concrete class. Finally, this class serves a conduit to connect our concrete class’s associated type with our base classes generic type parameter.

 

Public Wrapper

With our private implementation details in place, we need to create a public interface for our type erased wrapper. The naming convention used for this pattern is to prefix Any in front of the protocol you’re wrapping, in our case AnyRow.

This class has the following responsibilities:

  1. Conform to the Row protocol.
  2. Define a generic type parameter Model that serves as Rows associated type.
  3. Within its initializer, take a concrete implementer of the Row protocol.
  4. Wrap the concrete implementer in a private Box _AnyRowBase<Model>.
  5. Trampoline each Row function call along to the Box.

This final piece of the puzzle performs the actual type erasing. We supply the AnyRow class with a concrete implementer of Row (i.e. FileCell) and it erases that concrete type allowing us to work with simply any adopter of Row with matching associated types (i.e. AnyRow<File>).

 

Notice that while our concrete implementer is a property of type _AnyRowBase<Model>, we have to wrap it up in an instance of _AnyRowBox. The only requirement on _AnyRowBox’s initializer is that the concrete class implements Row. This is where the actual type easement occurs. Without this layer in our stack we’d be required to supply the associated type Model to _AnyRowBase<Model> explicitly; and we’d be back to square one.

The Payoff!

The biggest benefit to this process is all the messy boilerplate is an implementation detail. We are left with a relatively simple public API. Let’s revisit our original goals that are now possible with our type erased AnyRow<Model>.

 

Homogeneous Requirement

One important thing to note is we haven’t lost all of the conveniences and safety of strong typing. With Swift type checker still helping us out we cannot hold an array of just any AnyRow, our Model type must remain consistent.

This differs from how other more loosely typed languages such as Objective-C would handle the situation. The following is perfectly valid in Objective-C:

 

Swift, on the other hand, requires our array to be homogeneous around our protocol’s associated type. If we attempt the same line of code in Swift we’re presented with an error:

 

This is a good thing; we’re allowed enough flexibility to work with multiple types conforming to Row but we cannot be bitten by receiving a type we did not expect.

Wrapping Up

Assembling all the pieces required in the type erasure pattern can be overwhelming at first. Fortunately, it’s a formulaic process that will not differ based on your protocol. It’s such a systematic process that the Swift standard library may very well do this for you at some point. See the Swift Evolution’s Completing Generics Manifesto

To explore type erasure concepts in a Swift Playground, check out our accompanying Type Erasure Playgrounds:

https://www.bignerdranch.com/blog/breaking-down-type-erasures-in-swift/

Breaking Down Type Erasure in Swift的更多相关文章

  1. A “Type Erasure” Pattern that Works in Swift:类型域的转换

    新视角:通过函数式编程的范畴理论来看待这个问题会更容易理解: 在低层类型无法很好表达的类型,可以将其转化为高阶类型进行表示. 将协议的实现类型转化为monad类型: 解决将具有关联类型的协议当作类型的 ...

  2. 关于type erasure

    哇,好久没有写blog了,再不写的话,blog的秘密都要忘记了,嘿嘿. 最近在试着参与一个开源项目,名字叫avim(A Vibrate IM),别想多了哟.地址是:https://github.com ...

  3. Java魔法堂:解读基于Type Erasure的泛型

    一.前言 还记得JDK1.4时遍历列表的辛酸吗?我可是记忆犹新啊,那时因项目需求我从C#转身到Java的怀抱,然后因JDK1.4少了泛型这样语法糖(还有自动装箱.拆箱),让我受尽苦头啊,不过也反映自己 ...

  4. Type Erasure with Pokemon---swift的类型擦除

    我感觉这个是swift的设计缺陷. 类型擦除:解决泛型类型作为公用类型的问题 是抽象的公用机制的一种实现方式. 1)类型擦除并不能解决类型不一致的兼容问题,只能解决类似继承一致性的兼容问题. 2)擦除 ...

  5. Swift安装

    Server1 .Update sudo apt-get update sudo apt-get upgrade . sudo apt-get install bridge-utils .IP 3.1 ...

  6. swift中文文档- 类型转换

    未翻译完 待续(英语烂,求斧正) Type Casting 类型转换 Type casting is a way to check the type of an instance, and/or to ...

  7. OpenStack Swift集群与Keystone的整合使用说明

    之前已经介绍了OpenStack Swift集群和Keystone的安装部署,最后来讲一讲Swift集群与Keystone的整合使用吧. 1. 简介 本文档描述了Keystone与Swift集群的整合 ...

  8. Using Swift with Cocoa and Objective-C(Swift 2.0版):开始--基础设置-备

    这是一个正在研发的API或技术的概要文件,苹果公司提供这些信息主要是为了帮助你通过苹果产品使用这些技术或者编程接口而做好计划,该信息有可能会在未来发生改变,本文当中提到的软件应该以最终发布的操作系统测 ...

  9. 那些年,学swift踩过的坑

    最近在学swift,本以为多是语法与oc不同,而且都是使用相同的cocoa框架,相同的API,但是或多或少还是有些坑在里,为了避免以后再踩,在这里记下了,以后发现新的坑,也会慢慢在这里加上 [TOC] ...

随机推荐

  1. WPF备忘录(3)如何从 Datagrid 中获得单元格的内容与 使用值转换器进行绑定数据的转换IValueConverter

    一.如何从 Datagrid 中获得单元格的内容 DataGrid 属于一种 ItemsControl, 因此,它有 Items 属性并且用ItemContainer 封装它的 items. 但是,W ...

  2. vs2017 未能完成操作。不支持此接口

    打开vs2017开发者命令提示符 切换至安装下的指定目录 执行下面的命令就可以了    需要注意的是一定要用vs2017的开发人员命令提示符  别用cmd gacutil -i Microsoft.V ...

  3. SQL Server T—SQL 表连接

    一  笛卡尔积 select  *  from  表1,表2 将两表的记录遍历显示 二表的横向连接 1   使用外键关系作为条件 select  *  from   表1,表2  where   表1 ...

  4. 纯js实现文件下载并重命名功能

    直接记录代码: /** * 获取 blob * @param {String} url 目标文件地址 * @return {cb} */ function getBlob(url,cb) { var ...

  5. 了解java虚拟机—非堆相关参数设置(4)

    非堆内存相关配置 -XX:PermSize 永久区初始大小 -XX:MaxPermSize 永久区最大大小 在JDK1.8中使用-XX:MxMetaspaceSize配置永久区最大大小 -Xss 线程 ...

  6. mybatis-plus排除非表中字段

    使用 transient 修饰 private transient String noColumn; 使用 static 修饰 private static String noColumn; 使用 T ...

  7. 一些减少代码量、提高开发效率的利器(Java)

    Spring Boot mybatis-plus代码生成器和自带CRUD接口 lombok 库: Apache Commons & guava AOP Java8: stream & ...

  8. 设计模式原则(6)--Open-Closed Principle(OCP)--开闭原则

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.定义: 一个软件实体应当对扩展开放,对修改关闭.即软件实体应尽量在不修改原有代码的情况下进行扩展. 2.使用场 ...

  9. Windows7安装 nginx+php 后访问.php文件出现 “No input file specified.” 的解决办法

    在Windows7上安装了Nginx+PHP,参考教程为 https://www.cnblogs.com/anlia/p/5916758.html 启动 nginx 后,在浏览器中输入localhos ...

  10. Android 常见adb命令

    Android 常见adb命令 by:授客 QQ:1033553122 1.  查看所有已链接的设备 命令: adb devices   例: C:\Users\laiyu>adb device ...