2015-11-21 15 views
1

我正在將Java庫移植到Swift 2.0並且在泛型中遇到一些麻煩。有沒有什麼方法可以確定符合Swift 2中的通用協議的實例(運行時或編譯期間)的子協議一致性?

我有以下協議層次:

public protocol Graph { 
    typealias V: Hashable 
    typealias E: Hashable 

    func getAllEdges(sourceVertex: V, targetVertex: V) -> Set<E>? 
    func getEdge(sourceVertex: V, targetVertex: V) -> E? 
    func getEdgeFactory() -> EdgeFactory<V, E>? 
    func addEdge(sourceVertex: V, targetVertex: V) -> E? 
    func addEdge(sourceVertex: V, targetVertex: V, e: E) -> Bool 
    func addVertex(v: V) -> Bool 
    func containsEdge(sourceVertex: V, targetVertex: V) -> Bool 
    func containsEdge(e: E) -> Bool 
    func containsVertex(v: V) -> Bool 
    func edgeSet() -> Set<E> 
    func edgesOf(v: V) -> Set<E> 
    func removeAllEdges<T: CollectionType where T.Generator.Element == E>(edges: T) -> Bool 
    func removeAllEdges(sourceVertex: V, targetVertex: V) -> Set<E>? 
    func removeAllVertices<T: CollectionType where T.Generator.Element == V>(vertices: T) -> Bool 
    func removeEdge(sourceVertex: V, targetVertex: V) 
    func removeEdge(e: E) -> Bool 
    func removeVertex(v: V) -> Bool 
    func vertexSet() -> Set<V> 
    func getEdgeSource(e: E) -> V 
    func getEdgeTarget(e: E) -> V 
    func getEdgeWeight(e: E) -> Double 
} 

public protocol DirectedGraph: Graph { 
    func inDegreeOf(vertex: V) -> Int 
    func incomingEdgesOf(vertex: V) -> Set<E> 
    func outDegreeOf(vertex: V) -> Int 
    func outgoingEdgesOf(vertex: V) -> Set<E> 
} 

public protocol UndirectedGraph: Graph {  
    func degreeOf(vertex: V) -> Int 
} 

下面是類的定義,這會導致麻煩:

public class CrossComponentIterator 
    <V: Hashable, E: Hashable, D, G: Graph 
     where G.V == V, G.E == E> 
    : AbstractGraphIterator<V, E> 

也就是說,它應該初始化根據實際類型的變量中的一個方法通過圖 - DirectedGraph或UndirectedGraph。

我試圖通過聲明它執行功能的多個版本解決這個這樣的:

func createGraphSpecifics<DG: Graph where DG: DirectedGraph, DG.V == V, DG.E == E>(graph: DG) 
    -> CrossComponentIteratorSpecifics<V, E> 
{ 
    return DirectedSpecifics<V, E, DG>(graph: graph) 
} 

func createGraphSpecifics<UG: Graph where UG: UndirectedGraph, UG.V == V, UG.E == E>(graph: UG) 
    -> CrossComponentIteratorSpecifics<V, E> 
{ 
    return UndirectedSpecifics<V, E, UG>(graph: graph) 
} 

func createGraphSpecifics<GG: Graph where GG.V == V, GG.E == E>(graph: GG) 
    -> CrossComponentIteratorSpecifics<V, E> 
{ 
    fatalError("Unknown graph type instance") 
} 

但不幸的是,只有最後一個功能版本叫爲圖形(任何情況下,即使符合「將DirectedGraph」或‘UndirectedGraph’)

而且我知道,這可能是我可以通過協議的DirectedGraph,並UndirectedGraph轉換爲抽象類 (我指的是類與fatalError()中聲明的每個功能,因爲斯威夫特沒有按」解決這個問題t支持法律上的抽象類)。

但也許有另一個更優雅和Swifty解決方案?

在Java中,這是微不足道的 - 符合接口在運行時檢查:

if (g instanceof DirectedGraph<?, ?>) { 
    return new DirectedSpecifics<V, E>((DirectedGraph<V, E>) g); 
} else { 
    return new UndirectedSpecifics<V, E>(g); 
} 

編輯這裏是最少的代碼爲我想要實現:

protocol P { 
    // This typealias makes impossible to use 'P' 
    // (or its descendants) as a type. 
    // It can be used only as generic constraint. 
    typealias A 

    // myfunc is needed for compiler to infer 'A' 
    func myfunc(a: A) 
} 
protocol P1:P { 
    func p1specific(a: A) 
} 
protocol P2:P { 
    func p2specific(a: A) 
} 

struct S<T:P> { 
    init(t: T) { 
     // TODO: check if 't' conforms to 'P1', 'P2', both or neither 
    } 
} 

// Examples of concrete implementations of 'P1' and 'P2' 
struct S1<X>:P1{ 
    func myfunc(a: X) {} 
    func p1specific(a: X) {} 
} 
struct S2<X>:P2{ 
    func myfunc(a: X) {} 
    func p2specific(a: X) {} 
} 
+0

您是否找到解決方案?是的,請分享。謝謝 – user3441734

回答

0

Is there any way to determine sub-protocol conformance for instance conforming to generic protocol in Swift 2 (at runtime or during compilation)?

是。

這是我實現類型擦除的技巧,因此允許使用運行時。觀察_P

protocol _P { 
    static var _A: Any.Type { get } 
    func _myfunc(_a: Any) -> Void? 
} 

extension _P where Self: P { 

    static var _A: Any.Type { 
     return A.self 
    } 

    func _myfunc(_a: Any) -> Void? { 
     return (_a as? A).map(myfunc) 
    } 
} 

protocol P { 
    typealias A 

    func myfunc(a: A) 
} 

protocol _P1:_P { 
    func _p1specific(_a: Any) -> Void? 
} 

extension _P1 where Self: P1 { 
    func _p1specific(_a: Any) -> Void? { 
     return (_a as? A).map(p1specific) 
    } 
} 

protocol P1:_P1, P { 
    func p1specific(a: A) 
} 

protocol _P2:_P { 
    func _p2specific(_a: Any) -> Void? 
} 

extension _P2 where Self: P2 { 
    func _p2specific(_a: Any) -> Void? { 
     return (_a as? A).map(p2specific) 
    } 
} 

protocol P2:_P2, P { 
    func p2specific(a: A) 
} 

您現在可以確定的值是否符合P1P2,並迫使相應演員。此外,通用參數A現在可通過不透明Any.Type獲得。

(x as? _P1) != nil ? true : false

+0

和'解決方案'中的'通用參數實例'是什麼? – user3441734

+0

@ user3441734:我已經更新了我的答案。 –

+0

@ user3441734:你的downvote是不合理的。請看看我的編輯,希望這可以澄清任何疑問。 –

-1
import XCPlayground 
import Foundation 

protocol P {} 
protocol P1:P {} 
protocol P2:P {} 

struct S1:P1{} 
struct S2:P2{} 

struct S<T:P> { 
    var p1: P1? 
    var p2: P2? 

    init(t: T) { 
     p1 = t as? P1 
     p2 = t as? P2 
    } 
} 

let p1 = S1() 
let p2 = S2() 
let s1 = S(t: p1) 
let s2 = S(t: p2) 

dump(s1) 
dump(s2) 
/* 
▿ S<S1> 
    ▿ p1: S1 
    - Some: S1 
    - p2: nil 
▿ S<S2> 
    - p1: nil 
    ▿ p2: S2 
    - Some: S2 
*/ 

使用

g is Type    // trur or false 

let v2 = v1 as? Type // v2 = v2 or nil 

在迅速 更新

protocol P { 
    typealias A 
} 
protocol P1:P {} 
protocol P2:P {} 

struct S1:P1{ 
    typealias A = Int 
} 
struct S2:P2{ 
    typealias A = Double 
} 

struct S<T:P> { 
    var p1: S1? 
    var p2: S2? 

    init(t: T) { 
     p1 = t as? S1 
     p2 = t as? S2 
    } 
} 

let p1 = S1() 
let p2 = S2() 
let s1 = S(t: p1) 
let s2 = S(t: p2) 

.....

protocol P { 
    // This typealias makes impossible to use 'P' 
    // (or its descendants) as a type. 
    // It can be used only as generic constraint. 
    typealias A 

    // myfunc is needed for compiler to infer 'A' 
    func myfunc(a: A) 
} 
protocol P1:P {} 
protocol P2:P {} 

// aka 'abstract' conforming to P1 
struct S1:P1{ 
    typealias A = AnyObject 
    func myfunc(a: A) {} 
} 
// aka 'abstract' conforming to P2 
struct S2:P2{ 
    typealias A = Int 
    func myfunc(a: A) {} 
} 
// generic struct with type conforming to P 
struct S<T:P> { 
    init(t: T) { 
     // TODO: check if 't' conforms to 'P1', 'P2', both or neither 
     if t is S1 { 
      print("t conforms to P1, because it is type S1") 
     } 
     if t is S2 { 
      print("t conforms to P2, besause it is type S2") 
     } 
    } 
} 

let s1 = S(t: S1()) // t conforms to P1, because it is type S1 
let s2 = S(t: S2()) // t conforms to P2, besause it is type S2 

// WARNING !!!!!! 
// var s = s1 
// s = s2 // error: cannot assign value of type 'S<S2>' to type 'S<S1>' 
+1

你的代碼可以工作,但是如果你在「protocol P」中添加「typealias A」(即使其類似於Java中的通用接口),編譯過程中會出現以下錯誤:「error:protocol'P1'can only be used作爲通用約束,因爲它具有自我或相關類型要求「。即你不能使用「as?」因爲P1和P2不是一種類型,而是一般約束。 –

+0

有什麼麻煩?給我一個小而簡單的示例代碼。抽象類的概念並不在迅速發揮作用。 – user3441734

+0

當然。我爲這個問題添加了一個最簡單的例子。 –