泛型和返回专门泛型实例的方法



我正在尝试创建一个方法,该方法将返回某个泛型类型的专用实例。让我们假设以下示例:

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时产生错误。如果你也让FooBar成为final,那么我认为从假设的";"聪明";编译器。

最新更新