swift 如何定义抽象类以及为什么苹果发明关联类型但不使用通用协议(protocol)

标签 swift abstract-class generic-interface

我是一个快速的初学者。学习的时候有个困惑。现在我想定义一个抽象类或者定义一些纯虚方法,但是我找不到方法。我有一个关联类型的协议(protocol)(这个我也很纳闷,为什么不使用泛型协议(protocol)),有些方法需要在基类中实现,而其他继承自基类的类,他们应该在协议(protocol)中实现其他方法,我能怎么做? 例如:

Protocol P{
    typealias TypeParam
    func A()
    func B()
}

class BaseClass<TypeParam> : P {
    abstract func A()
    func B(){
        if someCondition {
            A()
        }
    }
}

class ChildClass : BaseClass<Int> {
    func A(){}
}

看起来很奇怪,我仍然找不到解决抽象问题的方法。

最佳答案

Swift 有类似的东西:协议(protocol)扩展

它们可以定义默认实现,因此您不必在基类中声明该方法,但它也不会强制在任何类、结构或枚举中执行此操作。

protocol P {
    associatedtype TypeParameter
    func A()
    func B()
}

extension P {
    func A(){}
}

class BaseClass<TypeParam> : P {
    typealias TypeParameter = TypeParam
    func B(){
        if someCondition {
            A()
        }
    }
}

class ChildClass : BaseClass<Int> {
    // implementation of A() is not forced since it has a default implementation
    func A(){}
}

另一种方法是使用协议(protocol)代替 BaseClass更符合面向协议(protocol)的编程:

protocol Base {
    associatedtype TypeParameter
    func A()
    func B()
}

extension Base {
    func B(){
        if someCondition {
            A()
        }
    }
}

class ChildClass : Base {
    typealias TypeParameter = Int

    // implementation of A() is forced but B() is not forced
    func A(){}
}

然而,最大的缺点之一是协议(protocol)类型的变量只能在泛型代码中使用(作为泛型约束):

var base: Base = ChildClass() // DISALLOWED in every scope

作为此限制的解决方法,您可以创建一个包装器类型:

// wrapper type
struct AnyBase<T>: Base {
    typealias TypeParameter = T
    let a: () -> ()
    let b: () -> ()
    init<B: Base>(_ base: B) where B.TypeParameter == T {
        // methods are passed by reference and could lead to reference cycles
        // below is a more sophisticated way to solve also this problem
        a = base.A
        b = base.B
    }
    func A() { a() }
    func B() { b() }
}

// using the wrapper:
var base = AnyBase(ChildClass()) // is of type AnyBase<Int>

关于“真正的”泛型协议(protocol)的使用,Swift 团队选择使用 associatedtype因为您可以使用许多泛型类型而不必全部写在括号中 <> .

例如Collection你在哪里有一个关联的 IteratorIndex类型。这允许您拥有特定的迭代器(例如 DictionaryArray )。

一般来说,泛型/关联类型有利于编译期间的代码优化,但同时有时过于静态,您必须使用泛型包装器类型。

一些有用的链接 patterns for working with associated types .


(参见上文)

解决通过引用传递方法问题的更复杂的方法。

// same as `Base` but without any associated types
protocol _Base {
    func A()
    func B()
}

// used to store the concrete type
// or if possible let `Base` inherit from `_Base`
// (Note: `extension Base: _Base {}` is currently not possible)
struct BaseBox<B: Base>: _Base {
    var base: B
    init(_ b: B) { base = b}
    func A() { base.A() }
    func B() { base.B() }
}

struct AnyBase2<T>: Base {
    typealias TypeParameter = T
    var base: _Base
    init<B: Base>(_ base: B) where B.TypeParameter == T {
        self.base = BaseBox(base)
    }
    func A() { base.A() }
    func B() { base.B() }
}

// using the wrapper:
var base2 = AnyBase2(ChildClass()) // is of type AnyBase2<Int>

关于swift 如何定义抽象类以及为什么苹果发明关联类型但不使用通用协议(protocol),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33098637/

相关文章:

python - 实例化抽象类时没有错误,即使没有实现抽象方法

c# - 接口(interface)通用层次结构

swift - 核心数据 - 首次打开时创建初始数据,然后将其加载到 NSTableView - OSX

ios - 我的背景音乐被播放了两次(同时)

Java多态方法无法解析

c# - 使用泛型接口(interface)作为方法或函数的类型参数

ios - 如何快速制作可设计的文本字段代码类

ios - 核心数据错误代码=134030 "An error occurred while saving"没有用户信息

java - 如何为子类提供静态字段?