2015-10-23 5 views
2

У меня есть базовый класс ItemBase и класс CollectionBase с товарами типа ItemBase.Свифт: Коллекция общих коллекций

Я получаю два класса из этого ItemBase - PersonItem и AnimalItem.

Я получаю два класса от CollectionBase с соответствующими товарами - PersonCollection и AnimalCollection.

И затем я создаю массив типа CollectionBase - ItemBase.

Теперь я хочу добавить к PersonCollection и AnimalCollection в массив, но я получаю следующее сообщение об ошибке:

"Cannot assign value of type 'PersonCollection' to type 'DataCollection"

Как я могу определить сбор-массив, что я могу добавить элементы, основанные на CollectionBase - ItemBase?

class ItemBase { 
} 

class CollectionBase<T: ItemBase> { 
    var Items = [T]() 
} 

class PersonItem: ItemBase { 
} 

class PersonCollection: CollectionBase<PersonItem> { 
} 

class AnimalItem: ItemBase { 
} 

class AnimalCollection: CollectionBase<AnimalItem> { 
} 

var collection = [CollectionBase<ItemBase>]() 

// Error: Cannot assign value of type 'PersonCollection' to type 'DataCollection<ItemBase> 
collection.append(PersonCollection()) 
// Error: Cannot assign value of type 'AnimalCollection' to type 'DataCollection<ItemBase> 
collection.append(AnimalCollection()) 

ответ

2

Во-первых, ваш пример содержит некоторые contradictary заявления о системе типа, что компилятор (правильно) не может принять.

Для этого 'aha moment', прочитайте на variance and contravariance on Wikipedia. В противном случае продолжайте чтение.


Причина ваш пример не может здесь, является то, что

var collection = [CollectionBase<ItemBase>]() 

определяет массив классов, к которым можно получить доступ и обновлять свойство Items члена с элементами класса ItemBase.

Затем вы пытаетесь добавить элемент массива PersonCollection(), который нарушает это требование - это не позволяет добавлять ItemBase к элементу Items.


Во-вторых, потому что

PersonCollection inherits from ItemBase

вы принимаете, как прочитал, что верно следующее:

SomeGeneric<PersonCollection> inherits from SomeGeneric<ItemBase>

в то время как это может сделать идеальный смысл для вас, нет ничего в скор язык, который делает это так.

Вы могли бы разумно указать здесь, что быстрые классы коллекции, кажется, управлять этим OK - следующим образом:

class ItemBase { 
} 

class PersonItem: ItemBase { 
} 

class AnimalItem: ItemBase { 
} 

var collection = [Array<ItemBase>]() 

collection.append([PersonItem]()) 

Что он делает, но, к сожалению, он делает это потому, что быстры язык имеет особые правила встроенные типы коллекций, такие как Array. As far as I know вы не могли самостоятельно реализовать массив, используя только быстрый код.


Как с этим справиться?

Пока вы не устраните вышеуказанные несоответствия в своем дизайне, вы не сможете. Однако я предполагаю, что эти функции являются лишь артефактами того, как вы подходили к проблеме, учитывая язык, который у вас есть.

Вы можете использовать встроенный класс массива Swift.

Или вы можете определить другую иерархию наследования, которая не является общим в базе:

Например - использовать протокол (или класс) на основании, что, безусловно, работает на объектах ItemBase (не связано типа).

protocol ItemCollection { 
    var items:[ItemBase] { get } 
} 

class ThingCollection<Thing:ItemBase>:ItemCollection { 
    var things:[Thing] = [] 
    var items:[ItemBase] { 
     return things 
    } 
} 

typealias PersonCollection = ThingCollection<PersonItem> 


var itemCollection:[ItemCollection] = [] 

itemCollection.append(PersonCollection()) 
0

Зачем вам нужна дополнительная косвенность? Что случилось с:

class ItemBase { 
} 

class PersonItem: ItemBase { 
} 

class AnimalItem: ItemBase { 
} 

var collection = [ItemBase]() 
collection.append(PersonItem()) 
collection.append(AnimalItem()) 

Или:

class ItemBase { 
} 

class PersonItem: ItemBase { 
} 

class AnimalItem: ItemBase { 
} 

var collection = [[ItemBase]]() 
collection.append([PersonItem]()) 

Что также можно:

class ItemBase { 
} 

class PersonItem: ItemBase { 
} 

class AnimalItem: ItemBase { 
} 

class CollectionBase<T: ItemBase> { 
    var Items = [T]() 
} 

class PersonCollection: CollectionBase<PersonItem> { 
} 

class AnimalCollection: CollectionBase<AnimalItem> { 
} 

let personCollection = PersonCollection() 
personCollection.Items.append(PersonItem()) 

Но я думаю, что дополнительные окольные вы хотите не представляется возможным в Swift. Вы можете решить эту проблему, но теряют строгую типобезопасность:

var collection = [AnyObject]() 
collection.append(PersonCollection()) 
collection.append(AnimalCollection()) 

if collection[0] is PersonCollection { 
    // ... 
} 
+0

В примере класс CollectionBase пуст, но класс обладает гораздо большей функциональностью. Поэтому мне это нужно. – Gerald

+0

Я добавил дополнительный вариант ответа. – Darko

+0

Спасибо Darko, но мне нужна PersonCollection и AnimalCollection в одной коллекции ... Я также думаю, что это невозможно. Я искал последние дни для решения, и этот пост - моя последняя надежда. – Gerald

Смежные вопросы