簡體   English   中英

Swift - 如何存儲在泛型類型的Array子類中

[英]Swift - How to store in an Array subclasses of a generic type

所以,我有一個泛型(有限制)類和它的子類的許多子類,當它們是子類時具體的泛型類型。

我想將這些子類的實例存儲在一個數組中,這樣它們就可以迭代並以相同的方式處理所有這些子類,但顯然,沒有辦法將子類轉換為泛型超類。

以下是一些說明問題的代碼(您可以將其復制粘貼到操場中以查看結果):

// 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類的數組,但編譯器抱怨。

您正在混淆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作為公共祖先,並使用動態類型來檢查子類。

PS:你的代碼有點難以理解,也許使用像'Fruit','Apple'和'Orange'這樣的東西 - 比'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的協議,它可以獲得一個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()]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM