Swift-如何存储在通用类型的数组子类中



因此,我有一个通用(具有限制)类,并且在子类子类时会混凝土类型的许多子类。

我想将这些子类的实例存储在数组中,这样就可以迭代并以相同的方式对其进行处理,但是显然,没有办法从子类转换为通用超级类。

这是一些说明问题的代码(您可以在操场上复制它以查看结果):

// 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:我有一个带有一些子类的通用类,我想拥有一个填充子类的通用类数组,但编译器抱怨。

您正在混合仿制药之前和之后的类型层次结构。使用通用类/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用作共同祖先,并使用动态类型检查子类。

P.S。:您的代码很难遵循,也许使用"水果","苹果"one_answers"橙色"之类的东西 - 比'SuperClass',subclass1','subclass2'; - )

我认为您正在寻找的是型擦除。

例如,成像您有以下内容:

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进行操作。

如果您想访问单个子类型,请在此函数上创建一个接受通用参数的函数,并且应该能够施放以访问子类型信息。

也许您可以定义一个名为FruitTree的协议,该协议可以得到水果并返回某种水果:

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()]

最新更新