2016-01-04 3 views
8

Скажем, у меня есть коллекция объектов, наследующих от общего суперкласса (это предпочтительнее протоколов в данном случае):Swift дженерики: возвращение типа на основе типа параметра

class ObjectSuperClass { 
    type: ObjectType 
} 
class ObjectClass1: ObjectSuperClass { 
    type = .Type1 
} 
class ObjectClass2: ObjectSuperClass { 
    type = .Type2 
} 

Я ищу, чтобы создать общий функция поиска, как это:

func objectsOfType<T: ObjectSuperClass>(T.class, otherFilter: Any?) -> [T] 

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

let result = objectsOfType(ObjectClass2.class, otherFilter: nil) -> [ObjectClass2] 
(pseudo-swift) 

Я чувствую, что это то, что может помочь генерация, но не может видеть, где должны быть помещены ограничения. Является ли это возможным?

ответ

11

Ну замечательно это работает ...

func filterType<T>(list: [AnyObject]) -> [T] 
{ 
    return list.filter{ $0 is T }.map{ $0 as! T } 
} 

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

class ObjectSuperClass: CustomStringConvertible 
{ 
    let myType: String 
    init(aString: String) 
    { 
     myType = aString 
    } 
    var description: String { return myType } 
} 

class ObjectClass1: ObjectSuperClass 
{ 
    init() 
    { 
     super.init(aString: "<t 1>") 
    } 
} 

class ObjectClass2: ObjectSuperClass 
{ 
    init() 
    { 
     super.init(aString: "<t 2>") 
    } 
} 

let unfilteredList: [AnyObject] = [ ObjectClass1(), ObjectClass2(), ObjectSuperClass(aString: "<Who knows>")] 

let filteredList1: [ObjectClass1] = filterType(unfilteredList) 
print("\(filteredList1)") // <t 1> 

let filteredList2: [ObjectClass2] = filterType(unfilteredList) 
print("\(filteredList2)") // <t 2> 

let filteredList3: [ObjectSuperClass] = filterType(unfilteredList) 
print("\(filteredList3)") // [<t 1>, <t 2>, <Who knows>] 

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


Если вы хотите «дополнительный фильтр» Вам не нужно явно вводить результаты, пока T может быть выведен из вашей дополнительной функции фильтра.

func extraFilterType<T>(list: [AnyObject], extraFilter: T -> Bool) -> [T] 
{ 
    return list.filter{ $0 is T }.map{ $0 as! T }.filter(extraFilter) 
} 

let filteredList = extraFilterType(unfilteredList){ 
    (element : ObjectClass2) -> Bool in 
    !element.description.isEmpty 
} 

print("\(filteredList)") // <t 2> 
+0

Это хорошо, + 1.Out из любопытства, вы могли бы использовать '.dynamicType' собственности даже сортировать только объекты суперкласса? Например, 'filterList3' в вашем примере выше, чтобы получить только' [] '? (Например, 'return list.filter {$ 0.dynamicType == T.self} .map {$ 0 as!T} 'не работает, но что-то в этом роде?) – dfri

+0

@dfri возможно. Я не проверял. Это не то, о чем я обычно беспокоюсь, потому что, если я не могу рассматривать объекты подкласса, как если бы они были объектами базового класса, я воспринимаю это как запах кода, указывающий на неправильный дизайн моего класса. – JeremyP

+0

@dfri На самом деле это работает. Вам просто нужно явно объявить параметр для первого закрытия фильтра как 'AnyObject' i.e.' return list.filter {(anObj: AnyObject) -> Bool в anObj.dynamicType == T.self} .map {$ 0 as! T} ' – JeremyP

1

Это ближе приближение я могу придумать:

func objectsOfType<T: ObjectSuperClass>(type type: T.Type) -> [T] { 
    // Just returns an array of all objects of given type 
} 

func objectsOfType<T: ObjectSuperClass>(type type: T.Type, predicate: T -> Bool) -> [T] { 
    // Uses predicate to filter out objects of given type 
} 

Использование:

let bar = objectsOfType(type: ObjectClass1.self) 

let baz = objectsOfType(type: ObjectClass2.self) { 
    // Something that returns Bool and uses $0 
} 

Технически, вы можете также пойти без type аргумента в выше, но тогда вам должны быть явно введенные приемники (bar и baz в приведенном выше примере), так что Swift может правильно вывести типы для вас и используйте правильную версию общей функции.

+0

Спасибо, если бы я мог принять несколько ответов, я бы хотел. – Ben

0

Вы можете реализовать функцию следующим образом:

func objectsOfType<T: ObjectSuperClass>(objects: [ObjectSuperClass], subclass: T.Type, otherFilter: (T->Bool)?) -> [T] { 
    if let otherFilter = otherFilter { 
     return objects.filter{$0 is T && otherFilter($0 as! T)}.map{$0 as! T} 
    } else { 
     return objects.filter{$0 is T}.map{$0 as! T} 
    } 
} 

Пример использования:

objectsOfType(arrayOfObjects, subclass: ObjectClass1.self, otherFilter: nil) 

Обратите внимание, что я не фанат принудительного литья, однако в этом случае она не должна вызывать проблем ,

Или, более подробная версия функции, с одной меньше форсированного броска:

func objectsOfType<T: ObjectSuperClass>(objects: [ObjectSuperClass], subclass: T.Type, otherFilter: (T->Bool)?) -> [T] { 
    return objects.filter({object in 
     if let object = object as? T { 
      if let otherFilter = otherFilter { 
       return otherFilter(object) 
      } else { 
       return true 
      } 
     } else { 
      return false 
     } 
    }).map({object in 
     return object as! T 
    }) 
}