所以,我有一个泛型(有限制)类和它的子类,它们在子类化时具有泛型类型。
我想将这些子类的实例存储在一个数组中,这样它们就可以以相同的方式迭代并处理所有子类,但显然,没有办法从子类转换为泛型超类。
以下是一些说明问题的代码(您可以将其复制粘贴到操场上以查看结果):
// Lets create regular classes
class Fruit {
var text: String { return "I am some Fruit" }
}
class Apple: Fruit {
override var text: String { return "I am an Apple" }
}
class Orange: Fruit {
override var text: String { return "I am an Orange" }
}
// This obviously works:
let test1: Fruit = Apple()
let test2: Fruit = Orange()
// Let's create some generic class
class Tree<T: Fruit> {
let fruit: T
init(fruit: T) {
self.fruit = fruit
}
}
// Subclasses from the generic class (these work)
class AppleTree: Tree<Apple> {
convenience init() {
self.init(fruit: Apple())
}
}
class OrangeTree: Tree<Orange> {
convenience init() {
self.init(fruit: Orange())
}
}
// This works:
let tree: Tree<Fruit> = Tree(fruit: Apple())
tree.fruit.text // "I am an Apple"
// This works:
let appleTree1: Tree<Apple> = AppleTree()
appleTree1.fruit.text // "I am an Apple"
// This fails: "Cannot convert value of type 'AppleTree' to specified type 'Tree<Fruit>'
let appleTree2: Tree<Fruit> = AppleTree()
// This works:
let fruitArray: [Fruit] = [Apple(), Orange()]
// THIS IS MY GOAL:
// This fails: "Cannot convert value of type 'AppleTree' to specified type 'Tree<Fruit>'
let treeArray: [Tree<Fruit>] = [AppleTree(), OrangeTree()]
// Let's try with a generic subclass
class FruitTree<T: Fruit>: Tree<T>{}
// This works:
let genericTree: Tree<Fruit> = FruitTree(fruit: Apple())
// Let's try with a generic but more concrete subclass
class GenericOrangeTree<T: Orange>: Tree<T>{
convenience init() {
self.init(fruit: Orange() as! T)
}
}
// This works:
let genericOrangeTree1 = GenericOrangeTree(fruit: Orange())
let genericOrangeTree2 = GenericOrangeTree()
// This fails: Cannot invoke initializer for type 'GenericOrangeTree<Orange>' with an argument list of type '(fruit: Orange)'
let genericTree2: Tree<Fruit> = GenericOrangeTree(fruit: Orange())
// Again, this fails: "Cannot convert value of type 'GenericOrangeTree<Orange>' to specified type 'Tree<Fruit>'
let genericTreeArray: [Tree<Fruit>] = [GenericOrangeTree()]
我试图做的事情在示例代码中由treeArray
变量说明。
我不明白代码失败时代码失败的原因。我的直觉说这应该有效,我无法找到解决这个问题的方法。
TL; DR:我有一个带有一些子类的Generic类,我想要一个用子类填充的Generic类数组,但编译器会抱怨。
答案 0 :(得分:0)
您正在混淆Generics实现之前和之后的类型层次结构。使用泛型类/ func,您基本上可以设置一个在编译时解析的模板(编译器宏)。
如果你说
Generic<SubClass1>
Generic<SuperClass>
编译器将这些解析为类似:
class Generic_SubClass1 {
let property : SubClass1
}
class Generic_SuperClass {
let property : SuperClass
}
在解析泛型后,这两种类型不共享基本类型,因此不能相互转换。它们是完全分开的。
不确定也没试过,但也许这就是你想要的:
class GenericBase {
let property : SuperClass
}
class Generic<T: SuperClass> : GenericBase {
final var typedProperty : T {
get { return property as T }
set { property = T }
}
}
然后,您可以使用GenericBase
作为共同祖先,并使用动态类型来检查子类。
答案 1 :(得分:0)
也许你可以定义一个名为FruitTree
的协议,它可以获得一个Fruit并返回某种Fruit:
protocol FruitTree {
associatedType FruitKind
func getFruit() -> Fruit
func setFruit(FruitKind)
}
然后,定义类如:
class AppleTree: FruitTree {
var apple Apple
typeAlias FruitKind = Apple
func getFruit() -> Apple {return apple}
func setFruit(Apple a} {apple = a}
}
class OrangeTree: FruitTree {
var orange Orange
typeAlias FruitKind = Orange
func getFruit() -> Orange { return orange}
func setFruit(Orange o) {orange= o}
}
let treeArray: [FruitTree] = [AppleTree(), OrangeTree()]
答案 2 :(得分:0)
我认为您正在寻找的是类型擦除。
例如,对您进行以下成像:
protocol ClassWithView {
associatedType: View: UIView
var view: View { get set }
}
class ConcreteWithView {
associatedType View = SubclassOfUIView
var view: SubclassOfUIView
}
// Somewhere this will fail because there is missing associated type information
var array: [ConcreteWithView]
使用类型擦除,您可以强制执行对数组的任何访问将只允许您访问常见的UIView内容。像上面这样修改:
protocol AnyClassWithView {
var baseView: UIView
}
protocol ClassWithView: AnyClassWithView {
associatedType: View: UIView
var view: View { get set }
}
// Default implementation
extension AnyClassWithView {
var baseView: UIView {
return view as UIView
}
}
现在,在其他地方,您可以定义数组:
var array: [AnyClassWithView]
这将成功,并且您只能使用.baseView
访问UIView类型。您可以将内容添加到AnyClassWithView
定义中,并使用共享的UIView
进行操作。
如果要访问各个子类型,请在其上创建一个接受通用参数的函数,并且应该能够进行强制转换以访问子类型信息。