我正在尝试创建一个方法,该方法将返回某个泛型类型的专用实例。让我们假设以下示例:
class Base { }
class Foo: Base {}
class Bar: Base {}
class MyGenericView<T> where T: Base {
func show(with item: T) { }
}
class MySpecializedViewFoo: MyGenericView<Foo> { }
class MySpecializedViewBar: MyGenericView<Bar> { }
有了上面给出的,我想有一个像这样的功能:
func createView<T: Base>(for item: T) -> MyGenericView<T>
但当我尝试像一样实现它时
func createView<T: Base>(for item: T) -> MyGenericView<T> {
if item is Foo {
return MySpecializedViewFoo()
} else if item is Bar {
return MySpecializedViewBar()
}
fatalError("Unsupported item")
}
然后我收到
"无法将类型"MyGenericView"的返回表达式转换为返回类型"MyGenericView"错误
这是可以实现的吗?有人能不能看一下,指出我对这一点的理解中的一个错误?
您可以通过执行以下操作强制使编译器信任您:
func createView<T: Base>(for item: T) -> MyGenericView<T> {
if item is Foo {
return MySpecializedViewFoo() as! MyGenericView<T>
} else if item is Bar {
return MySpecializedViewBar() as! MyGenericView<T>
}
fatalError("Unsupported item")
}
编译器不允许你这样做,因为它足够聪明,可以进行控制流分析,以确定方法任何给定点的泛型类型参数的边界。就它而言,return MySpecializedViewFoo()
在if语句内外的意思是一样的。
编译器不是这样设计的,因为无论如何都不应该让检查泛型类型参数的类型。
您应该使用两个重载:
func createView(for item: Foo) -> MyGenericView<Foo>
func createView(for item: Bar) -> MyGenericView<Bar>
事实上,你的检查一开始就不够充分,所以即使编译器足够聪明,它也会告诉你你所做的是不安全的。以下是一个如何出错的例子:
class FooSubclass: Foo {}
let foo = FooSubclass1()
let view: MyGenericView<FooSubclass1> = createView(foo)
在开始时使用修复程序,这将在运行时崩溃。createView
将尝试创建并返回一个不是MyGenericView<FooSubclass1>
的MySpecializedViewFoo
。Swift泛型是不变的。注意,在这种情况下,它将进入is Foo
分支,而不是fatalError
分支。
另一种情况是使用Base
作为T
:
let foo: Base = Foo()
let view: MyGenericView<Base> = createView(for: foo)
这将不会再次进入fatalError
分支。如果将Base
设为协议,则可以使其在T
被推断为Base
时产生错误。如果你也让Foo
和Bar
成为final
,那么我认为从假设的";"聪明";编译器。