2017-08-10 50 views
2

我有一個泛型類的形式爲:繼承,泛型和議定書的斯威夫特

class BaseClass<T> { 
    var prop: T 
    ... 
} 

我然後有形式的多個子類:

class SubClassOne: BaseClass<SomeSubClass> { 
    ... 
} 

class SubClassTwo: BaseClass<SomeOtherSubClass> { 
    ... 
} 

凡類型參數SomeSubClassSomeOtherSubClass都從一個共同的基類SomeBaseClass繼承。

我現在想定義一個變量來存儲SubClassOneSubClassTwo的實例。我已經嘗試了許多可能性:

var obj: BaseClass 
var obj: BaseClass<SomeBaseClass> 
var obj: BaseClass<Any> 

但第一次嘗試導致錯誤Reference to generic type 'BaseClass' requires arguments in <...>,另兩個結果在錯誤Cannot assign value of type 'SubClassOne' to type ...試圖分配一個值時。我甚至想雨燕編譯器欺騙的方式初始化數組的推斷類型對我來說:

var testArray = [SubClassOne(), SubClassTwo()] 

但是,即使這失敗了,導致錯誤Heterogeneous collection literal could only be inferred to [Any]; add explicit type annotation if this is intentional。實際上,成功允許存儲兩個子類的唯一類型註釋是AnyAnyObject。是否可以使用更具體的類型來存儲這些實例?如果不是,爲什麼?

這樣做很重要的原因是我最終想從存儲的變量obj中獲取屬性prop。如果obj存儲爲Any,我無法這樣做。我也無法簡單地將它投射到SubClassOneSubClassTwo,因爲我嘗試訪問屬性的方法本身是一種通用方法,要投射到的哪一個取決於方法的一般類型參數:

func castObj<T>(asType: T.Type) { 
    (self.obj as? T).prop 
} 

這將被稱爲:castObj(asType: SubClassOne.self)castObj(asType: SubClassTwo.self)。但是,我們遇到了同樣的問題:我可以定義的唯一的泛型類型參數約束同時接受SubClassOneSubClassTwoAny,然後Swift編譯器會報告:Value of type 'T' has no member 'prop'

作爲一種變通方法我試圖界定一個封裝所需性質的協議:

protocol HasProp { 
    var prop: SomeBaseClass { get } 
} 

然後,我已將此添加的SubClassOneSubClassTwo聲明。但是,這導致了另一個錯誤:Type 'SubClassOne' does not conform to protocol 'HasProp'。這也使我困惑,因爲SubClassOneSubClassTwo都從BaseClass<SomeSubClass>繼承prop,所以實際上符合協議。

總結:

  1. 是否有可能存儲SubClassOneSubClassTwo情況下,更具體的類型可以訪問的BaseClass屬性?如果不是,爲什麼?
  2. 爲什麼子類不符合預期的協議?
  3. 如何更改設計以達到我想要的行爲?

回答

0

問題是,此刻函數castObj對其通用參數T沒有類型約束。通過給出類型約束BaseClass你應該沒問題,因爲BaseClass有兩個屬性。

func castObj<T: BaseClass>(asType: T.Type) { 
    (self.obj as? T).propOne 
    (self.obj as? T).propTwo 
} 
+0

我試過這個了 - 我遇到了同樣的問題,當試圖首先爲'obj'定義一個類型時。簡單地'BaseClass'的類型約束不起作用,因爲'引用泛型類型'BaseClass'需要參數在<...>'中。如果我嘗試,例如'BaseClass ',我們又回到了原來的錯誤:當我嘗試調用方法時,無法將類型'SubClassOne.Type'的值轉換爲期望的參數類型'BaseClass .Type' 'castObj(asType:SubClassOne.self)' – asaini007

+0

這個函數是在'BaseClass'內還是外部定義的? –

+0

它被定義在它外面 – asaini007

0

在您的例子中,propTwo類型是常見的兩種亞型和propOne類型是專業。讓你的設計反映出來。

[是]

class BaseClass<T,U> { 
    var propOne: T 
    var propTwo: U 
    ... 
} 
class SubClassOne: BaseClass<SomeSubClass, SomeClass> {} 
class SubClassTwo: BaseClass<SomeOtherSubClass, SomeClass> {} 

[可]

class BaseClass<U> { 
    var propTwo: U 
    ... 
} 
class SubClassOne<T>: BaseClass<SomeClass> { 
    var propOne: T 
    ... 
} 
class SubClassTwo<T>: BaseClass<SomeClass> { 
    var propOne: T 
    ... 
} 

的一點是保持共同的東西在基類和撰寫你的專業。

+0

我不認爲我應該自己製作SubClassOne或SubClassTwo泛型,因爲它們都是專門爲SomeSubClass和SomeOtherSubClass定義的,使用這兩個類的方法和屬性。它們不是用不同的類型參數創建的。我同意這種方法可以訪問'propTwo',因爲這兩個SubClass都是'BaseClass '類型,但我不確定它會幫助訪問'propOne'。 – asaini007

+0

我也想過這個問題可以通過刪除第二個類型參數('U' /'SomeClass')來簡化。我認爲這是無關緊要的,也許與問題無關,但只是混淆了這個問題。 – asaini007

+0

已更新問題以反映此問題 – asaini007

0

SubclassOne和SubclassTwo在相同的繼承層次結構中存在根本的誤解。由於泛型類型,它們從不同的基類繼承而來。你不能混合和匹配它們。

想一想。有了繼承,你應該能夠使用任何的子類的任何地方,你有基類,所以在您的測試例如:

var testArray = [SubClassOne(), SubClassTwo()] 

將下列表達式的右手邊有哪些類型呢?

testArray [0] = .prop東西

而這一次

testArray [1] .prop =東西;

SubClassOne,的prop類型是SomeSubClassSubClassTwoprop類型必須爲SomeOtherSubClass

要使其發揮作用,唯一的方法是將prop聲明爲SomeBaseClass,並且不需要BaseClass爲通用。

編輯

爲什麼沒有協議的工作?

該協議的問題是,您將該屬性定義爲具有基類的類型,但它是可讀/寫的。協議實現中的屬性不能滿足與專用於其中一個子類的屬性的合同,因爲其他位代碼需要能夠將任何基類實例分配給該屬性。

protocol MyProtocol 
{ 
    var prop: BaseClass 
} 

struct MyImplementation: MyProtocol 
{ 
    var prop: SubClass 
} 

class BaseClass {} 
class SubClass: BaseClass {} 
class DifferentSubClass: BaseClass {} 

var instance: MyProtocol = MyImplementation() 

instance.prop = DifferentSubClass() 
// Should be legal because the protocol says so but the type of prop in instance is SubClass. 
+0

任何想法爲什麼協議不起作用? – asaini007

+0

@ asaini007是的。我將編輯答案 – JeremyP

+0

謝謝,這對於具有既可以設置又可設置的屬性的協議來說肯定是個問題。但奇怪的是,Swift甚至不允許具有隻讀屬性的協議由具有相同名稱屬性的類和類型爲該協議屬性類型的子類(如果有意義的話)符合。我真的不明白爲什麼這是不應該被禁止的。例如:http://swift.sandbox.bluemix.net/#/repl/5991c36977dbb8209e7b17c1 – asaini007