2017-08-10 52 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