Swift继承自泛型类型

13

我正在尝试从一个泛型类型继承,以便我可以将一个类型插入到层次结构中:

class Foo < T:AnyObject > : T {}

但是我遇到了错误

inheritance from non-protocol, non-class type 'T'

但我确保 T 是一个类(即使我将 AnyObject 更改为其他类)。这是否不可能?


对的,你不能从泛型的子类型继承。你可以使用装饰器模式代替继承来实现你想要的功能吗? - Nate Cook
我想要做的是更改一个继承自NSViewController的类,使其继承自Foo,而Foo又继承自NSViewController,但在将来如果我想从不同的类继承,也可以使用Foo。Foo有成员变量,所以我不能使用扩展,并且我不确定如何使用装饰器来实现这一点。 - zacaj
Swift似乎不支持C++、Java和.Net中常见的奇异递归模板模式(Curiously Recurring Template Pattern)。这可能是因为它不使用调用点类型推断,尽管我不知道Java和.Net是否也有这个限制。虽然如此,这仍然是一个很好且有用的特性。 - Chris Conover
我在开发论坛上发布了一个关于这个主题的问题:https://devforums.apple.com/thread/270342 - Chris Conover
2个回答

1

CRTP (Curiously recurring template pattern)和OP中的不同,对吗?

在Swift (<= 3.0)中设计该模式完全没有问题,但目前存在错误,在运行时会在初始化时锁定,而没有任何错误。

class Template<T> { ... }

class RealThing : Template<RealThing> { ... }

最近我在桥接的UIKit API中发现了这个模式。你可以在我的短博客文章这里中阅读。

我还将维基百科上的示例翻译成了Swift 3.0:

protocol Constructor {
    init()
}

protocol Shape {
    func cloned() -> Shape
}

typealias ShapeProtocols = Constructor & Shape

class Shape_CRTP<T> : ShapeProtocols {

    required init() {}

    func  cloned() -> Shape {
        let new = Shape_CRTP<T>()
        // copy everything to `new` from `self`
        return new
    }
}

extension Shape_CRTP where T : ShapeProtocols {

    func cloned() -> Shape {
        let new = T()
        // copy everything to `new` from `self`
        return new
    }
}

// First non-crtp proof of concept
class Square : ShapeProtocols {

    required init() {}
    func cloned() -> Shape {
        return Square()
    }
}

let clone = Shape_CRTP<Square>().cloned() // creates a new `Shape`
type(of: clone) // Square.Type

// now CRTP but this is bugged: http://blog.devandartist.com/posts/swift-crtp
class Circle : Shape_CRTP<Circle> {}

Circle().cloned()

print("this will never print because of the described bug")

问题不是关于CRTP。 - mxcl

0

如果你不知道某个东西是什么,那么你就不能从它继承,这有多种原因。

  • 首先,编译器需要知道如何在内存中布置你的类的实例。为了做到这一点,它需要知道哪些实例变量(存储属性)它有:包括那些类指定的和从超类继承的。

  • 编译器还需要了解超类的属性和初始化程序,以确保你的类的实例被正确初始化。如果超类的属性是未知的,那么在初始化程序结束之前,就无法知道是否已经初始化了所有应该初始化的状态(而没有初始化任何不应该初始化的状态)。如果超类的初始化程序是未知的,编译器无法强制你从自己的初始化程序中调用超类的指定初始化程序。

  • 如果你不知道超类是什么,那么你将如何编写引用超类属性和方法的方法实现?如果你编写引用self.view的代码,然后以某种方式实例化一个不继承具有该属性的类的版本,那么该代码将会出错。

可能还有几个原因,但这些应该足够了。 :)

尽管有些地方需要使用子类,但Cocoa(无论是在Swift还是ObjC中)通常更喜欢组合而不是继承进行定制。

如果您要向各种视图控制器添加大量功能,并通过将其分解为自己的类来重用该代码,则简单的解决方案是使您的视图控制器类都拥有该类的实例,而不是尝试成为该类。如果您有想要强制执行某些视图控制器类必须拥有Foo的位置,则可以定义表达该要求的协议。

如果您要向现有类添加功能,则可以使用扩展。您可能无法使用扩展添加存储属性,但您肯定可以模拟它。例如,您可以使用计算属性,并在其实现中定义自己的存储。其中一种方法可能是使用ObjC运行时的关联对象特性(据我所知,仍可从Swift访问)。


我不同意以下的理由:T的类型在编译时已知。在派生类实例化时,编译器已经知道了所有需要的类型。在C++中,这是一种常见的模式,没有涉及到动态类型。我真的看不出为什么Swift(当然是将来的版本)不能实现这一点的任何理由。 - ithron
1
好的,TBF T 在这个库代码中并不为人所知。然而,如果该类在范围上最多为 internal,那么这种情况可以避免。 - mxcl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接