Невозможно преобразовать универсальный подкласс swift в универсальный суперкласс - PullRequest
1 голос
/ 01 июля 2019

Я хочу писать более качественный и чистый код с использованием параметризованных классов в 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 ]

1 голос
/ 01 июля 2019

Я согласен с Константином в фундаментальной проблеме здесь. Этот код просто неверен, и Свифт говорит вам об этом. CustomAdapter.doSmth не может принять произвольный ETableViewCell, но adapter утверждает, что должен.

Есть много решений, в зависимости от конкретной проблемы, которую вы пытаетесь решить. Вы указали, что хотите написать «лучший и более чистый код». Это говорит о том, что у вас есть существующий код, в котором вы обнаружите чрезмерное дублирование или приведение. Итак, что вы хотите сделать, это посмотреть на этот код и посмотреть, какой код дублируется, а затем мы можем помочь вам разработать общие решения, чтобы избежать этого дублирования. На этот вопрос нет универсального ответа; выбор абстракции, который вы делаете в одном направлении, сделает другие направления менее гибкими. Абстракция - это выбор; они должны быть сделаны в контексте.

Как правило, в Swift вы должны избегать использования подклассов. Есть некоторые, которые требуются из-за привязки к ObjC, но Swift-ориентированный код должен избегать подклассов. В вашем конкретном примере интересный класс имеет только одну функцию. Если это действительно так, то реализовать это легко. Используйте одну функцию:

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, использоваться там, где требуется функция, которая принимает любой ETableViewCell. Это невозможно. Это не ограничение в Swift; это просто по типу невозможно. Лучшее, что вы можете сделать, это добавить «ничего не делать» или «сбой», как объясняет Константин.

Если вы сможете немного подробнее определить, какую именно проблему в существующем коде вы пытаетесь исправить, мы, вероятно, поможем вам разработать более эффективные решения. Добавление дженериков не делает ваш код «лучше или чище» сам по себе (и, по моему опыту, большинству лучших решений вообще не нужны дженерики).

1 голос
/ 01 июля 2019

Даже если 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) {}
}

остальная часть кода будет такой же, как у вас уже есть, только без ошибки времени компиляции.

0 голосов
/ 01 июля 2019

Давайте попробуем разобраться в некоторых фактах.

  • Допустим, у нас есть универсальный класс 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 получают специальную ковариационную диспенсацию, но она запечатлена в языке; вы не можете использовать такую ​​же диспенсацию.)

...