我想在Swift中使用参数化的类编写更好,更简洁的代码,但出现一个奇怪的构建错误:
Cannot convert value of type 'CustomAdapter' to expected argument type 'TableTestParametrizedAdapter<ETableViewCell>'
我真正想要的是能够使用一种方法(在适配器子类中重写)创建基本适配器类,该方法用于将单元格子类与相应的数据模型绑定在一起,并且每次都无需进行强制转换。
我将在下面发布代码,以便更好地理解我的意思。
class TestParametrizedAdapter<C>: NSObject {
func doSmth(cell: C) {
}
}
class TableTestParametrizedAdapter<C>: TestParametrizedAdapter<C> where C:ETableViewCell {
}
class PeopleTableViewCell: ETableViewCell {
}
class CustomAdapter: TableTestParametrizedAdapter<PeopleTableViewCell> {
override func doSmth(cell: PeopleTableViewCell) {
}
}
class TestBaseController: UIViewController {
var adapter: TableTestParametrizedAdapter<ETableViewCell>?
override func viewDidLoad() {
super.viewDidLoad()
setAdapter(adapter: CustomAdapter()) // this is the line with build error
}
func setAdapter(adapter: TableTestParametrizedAdapter<ETableViewCell>) {
self.adapter = adapter
}
}
我还阅读了其他文章,并指出GenericClass<B>
和GenericClass<A>
完全无关,即使B是A的子类,因此也不能将它们彼此转换。 (https://stackoverflow.com/a/50859053/10115072)
Anywat,对此有什么解决方案吗?在这种情况下,我们如何利用Swift的参数化功能?我使用Swift 4。
谢谢。
答案 0 :(得分:1)
即使Swift支持自定义泛型中的变量,您的代码也将是错误的,因为您尝试使用只能处理PeopleTableViewCell实例的对象代替可以处理任何ETableViewCell的对象。如果这确实是您想要的,并且您不介意进行某些运行时检查,则可以执行类似的操作,并且类型擦除很少:
class TestAnyAdapter: NSObject {
func doSmth(cell: Any) {}
}
class TableTestParametrizedAdapter<C>: TestAnyAdapter where C:ETableViewCell {
override func doSmth(cell: Any) {
guard let cell = cell as? C else {
return
}
self.doSmth(cell: cell)
}
func doSmth(cell: C) {}
}
其余代码将与您已有的代码相同,只是没有编译时错误。
答案 1 :(得分:1)
我同意康斯坦丁所说的根本问题。这段代码根本不正确,Swift告诉您。 Call<ResponseBody> sendNotification(@Body MyRequestBodyModel model);
不能接受任何任意的CustomAdapter.doSmth
,但ETableViewCell
声称必须接受。
有很多解决方案,具体取决于您要解决的实际问题。您表示要编写“更好的代码”。这表明您有现有代码,在其中发现过多的重复或强制转换。因此,您要做的就是查看该代码,然后查看正在重复的代码,然后我们可以帮助您设计通用解决方案以避免重复。这个问题没有普遍的答案。您在一个方向上做出的抽象选择会使其他方向的灵活性降低。抽象就是选择。它们需要根据上下文来制作。
在Swift中,通常应避免依赖子类化。由于与ObjC的桥接,因此有一些要求,但是以Swift为重点的代码应避免使用子类。在您的特定示例中,有趣的类只有一个功能。如果确实如此,则实现起来很容易。使用一个功能:
adapter
“但是我真正的问题要复杂得多!”好。然后,我们必须谈论您的真正问题。正确地将这些事物抽象为最简单的形式应该导致最简单的解决方案。如果事情实际上有点复杂,则可以使用结构和协议。
func customAdapter(cell: PeopleTableViewCell) {}
class TestBaseController: UIViewController {
let adapter: (PeopleTableViewCell) -> Void = customAdapter
}
我正在解决您可能要问的问题,即在需要接受任何protocol Adapter {
associatedtype Cell: UITableViewCell
func doSmth(cell: Cell)
}
struct CustomAdapter<Cell: ETableViewCell>: Adapter {
func doSmth(cell: Cell) {}
}
class TestBaseController: UIViewController {
let adapter: CustomAdapter<PeopleTableViewCell> = CustomAdapter()
}
的函数的情况下,如何使仅接受PeopleTableViewCell
的函数使用。这不可能。在Swift中这不是一个限制。这在类型上是不可能的。康斯坦丁解释说,最好的办法是添加“不执行任何操作”或“崩溃”。
如果您可以在要解决的现有代码中进一步确定哪些特定问题,我们可能会帮助您设计更好的解决方案。添加泛型并不会使您的代码本身变得“更好或更干净”(根据我的经验,大多数最佳解决方案几乎根本不需要泛型)。
答案 2 :(得分:0)
让我们弄清楚事实。
假设我们有通用类C<T>
。
我们还要说我们有类D和D2,其中D2是T的子类。
然后C<D2>
不是C<D>
的子类。它们只是单独的类型。 (我们说没有协方差。)
C<T>
有一个子类C2<T>
。然后C2<D>
是C<D>
的子类,而C2<D2>
是C<D2>
的子类。
只要参数化类型相同,就存在多态性。但是即使参数化类型不同,即使参数化类型是类和子类,也没有协方差。
(Swift Optional和Swift集合在这里得到特殊的协方差分配,但这已经融入到语言中了;您不能使用相同的分配。)