无法将快速通用子类转换为通用超类

时间:2019-07-01 14:00:50

标签: swift xcode generics

我想在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。

谢谢。

3 个答案:

答案 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集合在这里得到特殊的协方差分配,但这已经融入到语言中了;您不能使用相同的分配。)