我有一个带有typealias的协议:
@Override
public void draw(Canvas canvas) {
int sc = canvas.saveLayer(null, null,
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
);
canvas.drawCircle(vinylCenter[0],vinylCenter[1],radius,mPaint);
mDrawable.draw(testCanvas);
canvas.drawBitmap(src,0f,0f,xfermodePaint);
canvas.restoreToCount(sc);
}
和符合该协议的类:
protocol Archivable {
typealias DataType
func save(data: DataType, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> DataType
}
我想在另一个类中使用class Archiver: Archivable {
typealias DataType = Int
func save(data: DataType, withNewName newName: String) throws {
//saving
}
func load(fromFileName fileName: String) throws -> DataType {
//loading
}
}
作为属性,如:
Archivable
但是
失败了协议'Archivable'只能用作通用约束,因为它具有Self或相关类型要求
我的目标是class TestClass {
let arciver: Archivable = Archiver() //error here: Protocol 'Archivable' can only be used as a generic constraint because it has Self or associated type requiments
}
只应将TestClass
看作Archiver
,所以如果我想更改保存/加载机制,我只需要创建一个符合Archiveable
将其设置为Archivable
中的属性,但我不知道这是否有效,如果是,那么如何。
我想避免使用TestClass
而不是DataType。
答案 0 :(得分:3)
根据您实际尝试的操作,这可以使用类型擦除。如果您按照评论中发布的链接R Menke中的说明操作,则可以实现您的目标。由于TestClass
中的属性似乎是let,我假设你已经知道编译时DataType
的类型。首先,您需要设置一个类型已删除的Archivable
类,如下所示:
class AnyArchiver<T>: Archivable {
private let _save: ((T, String) throws -> Void)
private let _load: (String throws -> T)
init<U: Archivable where U.DataType == T>(_ archiver: U) {
_save = archiver.save
_load = archiver.load
}
func save(data: T, withNewName newName: String) throws {
try _save(data, newName)
}
func load(fromFileName fileName: String) throws -> T {
return try _load(fileName)
}
}
就像Swift的AnySequence
一样,你可以将Archiver
包含在TestClass
这个类中,如下所示:
class TestClass {
let archiver = AnyArchiver(Archiver())
}
通过类型推断,Swift会输入TestClass
'archiver,将常量设为AnyArchiver<Int>
。这样做将确保您不必创建十几个协议来定义DataType
StringArchiver
,ArrayArchiver
,IntArchiver
等等。相反,您可以选择使用这样的泛型来定义变量:
let intArchiver: AnyArchiver<Int>
let stringArchiver: AnyArchiver<String>
let modelArchiver: AnyArchiver<Model>
而不是像这样重复代码:
protocol IntArchivable: Archivable {
func save(data: Int, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> Int
}
protocol StringArchivable: Archivable {
func save(data: String, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> String
}
protocol ModelArchivable: Archivable {
func save(data: Model, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> Model
}
let intArchiver: IntArchivable
let stringArchiver: StringArchivable
let modelArchiver: ModelArchivable
我写了一篇关于goes into even more detail的帖子,以防你遇到这种方法的任何问题。我希望这有帮助!
答案 1 :(得分:1)
当您尝试声明并指定archiver
时:
let archiver: Archivable = Archiver()
它必须具体。
Archivable
不是具体类型,因为它的协议具有关联类型。
来自&#34; Swift编程语言(Swift 2)&#34;书:
关联类型为类型提供占位符名称(或别名) 用作协议的一部分。要使用的实际类型 在采用协议之前,不会指定关联类型。
因此,您需要声明从Archivable
继承并指定关联类型的协议:
protocol IntArchivable: Archivable {
func save(data: Int, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> Int
}
然后你可以采用这个协议:
class Archiver: IntArchivable {
func save(data: Int, withNewName newName: String) throws {
//saving
}
func load(fromFileName fileName: String) throws -> Int {
//loading
}
}
现在Swift中没有真正的通用协议,所以你不能像这样声明archiver
:
let archiver: Archivable<Int> = Archiver()
但事情是你不需要这样做,我解释原因。
来自&#34; Swift编程语言(Swift 2)&#34;书:
协议定义了适合特定任务或功能的方法,属性和其他要求的蓝图。
所以基本上当你想将archiver
声明为Archivable<Int>
时,你的意思是你不想使用archiver
来获知某些代码来了解它的具体类,并且访问其他方法,属性等
很明显,这段代码应该包含在单独的类,方法或函数中,archiver
应该作为参数传递给它,这个类,方法或函数将是通用的。
如果您通过初始化参数传递TestClass
,则archivable
可以是通用的:
class TestClass<T, A: Archivable where A.DataType == T> {
private let archivable: A
init(archivable: A) {
self.archivable = archivable
}
func test(data: T) {
try? archivable.save(data, withNewName: "Hello")
}
}
或者它可以使用接受archivable
作为参数的通用方法:
class TestClass {
func test<T, A: Archivable where A.DataType == T>(data: T, archivable: A) {
try? archivable.save(data, withNewName: "Hello")
}
}
答案 2 :(得分:-1)
赫克托尔提供了一个更复杂但最终更好的解决方案,但我认为无论如何我都会发布另一种选择。它更简单,但从长远来看可能不那么灵活。
typealias DataType = Int
protocol Archivable {
var data: DataType { get set }
func save(data: DataType, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> DataType
}
class Archiver: Archivable {
var data:DataType = 0
func save(data: DataType, withNewName newName: String) throws {
//saving
}
func load(fromFileName fileName: String) throws -> DataType {
return data
}
}
class TestClass {
let arciver: Archivable = Archiver()
}