简体   繁体   English

无法将Swift通用子类转换为通用超类

[英]Cannot convert swift generic subclass to generic superclass

I want to write better and cleaner code using parametrized classes in Swift but I'm getting a strange build error: 我想使用Swift中的参数化类编写更好,更简洁的代码,但出现一个奇怪的构建错误:

Cannot convert value of type 'CustomAdapter' to expected argument type 'TableTestParametrizedAdapter<ETableViewCell>'

What I actually want is to be able to create a base adapter class with one method (overridden in adapter subclasses) used to bind cell subclasses with the corresponding data model and get rid of casting every time. 我真正想要的是能够使用一种方法(在适配器子类中重写)创建基本适配器类,该方法用于将单元格子类与相应的数据模型绑定在一起,并且每次都摆脱铸造。

I'll post the code below, in order to understand better what I mean. 我将在下面发布代码,以便更好地理解我的意思。

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
    }
}

I have read on some other posts about this and there was pointed out that GenericClass<B> and GenericClass<A> are completely unrelated even if B is a subclass of A, hence you cannot cast one to the other. 我已经读过其他文章,有人指出,即使B是A的子类, GenericClass<B>GenericClass<A>也完全不相关,因此您不能将它们相互转换。 ( https://stackoverflow.com/a/50859053/10115072 ) https://stackoverflow.com/a/50859053/10115072

Anywat, are there any solutions for this? Anywat,对此有什么解决方案吗? How can we use the power of parametrization of Swift in this case? 在这种情况下,我们如何利用Swift的参数化功能? I use Swift 4. 我使用Swift 4。

Thanks in advance. 提前致谢。

Even if Swift would support variance in custom generics your code would be wrong, since you try to use object that can only handle PeopleTableViewCell instances in place of object that can handle any ETableViewCell. 即使Swift支持自定义泛型中的变量,您的代码也将是错误的,因为您尝试使用只能处理PeopleTableViewCell实例的对象代替可以处理任何ETableViewCell的对象。 If that is indeed what you want and you don't mind some run-time checks you can do something similar with little type erasure: 如果这确实是您想要的,并且您不介意进行某些运行时检查,则可以执行类似的操作,但类型擦除很少:

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) {}
}

the rest of the code will be the same as you already have, only without compile-time error. 其余代码将与您已有的代码相同,只是没有编译时错误。

I agree with Konstantin about the fundamental problem here. 我同意康斯坦丁所说的根本问题。 This code is simply incorrect, and Swift is telling you so. 这段代码根本不正确,Swift告诉您。 CustomAdapter.doSmth cannot accept any arbitrary ETableViewCell , but adapter claims it must. CustomAdapter.doSmth不能接受任何任意ETableViewCell ,但是adapter声称必须。

There are many solutions, depending on the actual problem you're trying to solve. 有很多解决方案,具体取决于您要解决的实际问题。 You indicated you want to write "better and cleaner code." 您表示要编写“更好的代码”。 That suggests you have existing code where you're finding excessive duplication or casting. 这表明您已经在现有代码中发现过多的重复或强制转换。 So what you want to do is look at that code, and see what code is being duplicated, and then we can help you design generic solutions to avoid that duplication. 因此,您要做的就是查看该代码,然后查看正在重复的代码,然后我们可以帮助您设计通用解决方案以避免重复。 There is no universal answer to this question; 这个问题没有普遍的答案。 abstraction choices you make in one direction will make other directions less flexible. 您在一个方向上做出的抽象选择会使其他方向的灵活性降低。 Abstraction is choices; 抽象就是选择; they need to be made in context. 它们需要根据上下文进行制作。

As a rule in Swift, you should avoid relying on subclassing. 通常,在Swift中,应该避免依赖子类化。 There is some that is required, because of bridging to ObjC, but Swift-focused code should avoid subclasses. 由于与ObjC的桥接,因此有一些要求,但是以Swift为重点的代码应避免使用子类。 In your particular example, the interesting class has just one function. 在您的特定示例中,有趣的类只有一个功能。 If that's really true, then implementing it is easy. 如果确实如此,则实现起来很容易。 Use one function: 使用一个功能:

func customAdapter(cell: PeopleTableViewCell) {}

class TestBaseController: UIViewController {
    let adapter: (PeopleTableViewCell) -> Void = customAdapter
}

"But my real problem is more complex than that!" “但是我真正的问题要复杂得多!” Ok. 好。 Then we have to talk about your real problem. 然后,我们必须谈论您的真正问题。 Abstracting these things down to their simplest forms rightly should lead to the simplest solutions. 正确地将这些事物抽象为最简单的形式应该导致最简单的解决方案。 If things are actually a bit more complex, you could use a struct and a protocol. 如果事情实际上有点复杂,则可以使用结构和协议。

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()
}

I'm glossing over what may be your question, which is how to make a function that only accepts PeopleTableViewCell be used where a function that accepts any ETableViewCell is required. 我想知道您的问题是什么,那就是在需要接受任何ETableViewCell的函数的情况下,如何使用仅接受PeopleTableViewCell的函数。 That's impossible. 这不可能。 It's not a limitation in Swift; 在Swift中这不是一个限制。 it's just type-wise impossible. 这在类型上是不可能的。 The best you could do is add "do nothing" or "crash" as Konstantin explains. 正如Konstantin解释的那样,您可能会做的最好的事情是添加“什么都不做”或“崩溃”。

If you can nail down a little more what particular problem in your existing code you're trying to fix, we can probably help you design better solutions. 如果您可以进一步确定正在尝试解决的现有代码中的特定问题,我们可能会帮助您设计更好的解决方案。 Adding generics does not make your code "better or cleaner" by themselves (and most of the best solutions barely need generics at all in my experience). 添加泛型并不会使您的代码本身变得“更好或更干净”(根据我的经验,大多数最佳解决方案几乎根本不需要泛型)。

Let's try to get some facts straight. 让我们尝试弄清事实。

  • Let's say we have generic class C<T> . 假设我们有通用类C<T>

  • And let's also say we have classes D and D2, where D2 is the subclass of T. 还要说我们有类D和D2,其中D2是T的子类。

Then C<D2> is not a subclass of C<D> . 那么C<D2> 不是 C<D>的子类。 They are just separate types. 它们只是单独的类型。 (We say there is not covariance .) (我们说没有协方差 。)

  • Let's say our generic class C<T> has a subclass C2<T> . 假设我们的通用类C<T>有一个子类C2<T>

Then C2<D> is a subclass of C<D> , and C2<D2> is a subclass of C<D2> . 然后C2<D>是的一个子类C<D> ,和C2<D2>是的一个子类C<D2>

So as long as the parameterized types are the same, there's polymorphism. 因此,只要参数化类型相同,就存在多态性。 But there's no covariance if the parameterized types are different, even if parameterized types are class and subclass. 但是,即使参数化类型是类和子类,如果参数化类型不同,也没有协方差。

(Swift Optional and Swift collections get a special covariance dispensation here, but that's baked into the language; you can't the same dispensation.) (Swift Optional和Swift集合在这里得到了特殊的协方差分配,但这已经融入到语言中了;您不能使用相同的分配。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM