Использование какого-либо протокола в качестве конкретного типа, соответствующего другому протоколу, не поддерживается

Я пытаюсь смешать дженерики с протоколами, и мне очень трудно

У меня есть определенная архитектура, реализованная в проекте Android/Java, и я пытаюсь переписать ее, чтобы она соответствовала проекту swift/iOS. Но я нашел это ограничение.

ProtocolA

protocol ProtocolA {

}

ProtocolB

protocol ProtocolB : ProtocolA {

}

ImplementProtocolA

class ImplementProtocolA <P : ProtocolA> {

    let currentProtocol : P

    init(currentProtocol : P) {
        self.currentProtocol = currentProtocol
    }

}

ImplementProtocolB

class ImplementProtocolB : ImplementProtocolA<ProtocolB> {

}

Итак, когда я пытаюсь установить ProtocolB как конкретный тип, который реализует ProtocolA, я получаю эту ошибку:

Использование ProtocolB в качестве конкретного типа, соответствующего протоколу ProtocolA, не поддерживается.

1 Есть ли причина для такого "ограничения"?

2 Есть ли способ обойти это?

3 Будет ли это поддерживаться в какой-то момент?

--UPDATED--

Еще один вариант этой же проблемы, я думаю:

Просмотр протоколов

protocol View {

}

protocol GetUserView : View {
    func showProgress()
    func hideProgress()
    func showError(message:String)
    func showUser(userDemo:UserDemo)
}

Протоколы докладчика

protocol Presenter {
    typealias V : View
}

class UserDemoPresenter : Presenter {
    typealias V = GetUserView
}

Ошибка:

UserDemoPresenter.swift Возможно, предполагаемое совпадение 'V' (также называемое 'GetUserView') не соответствует 'View'

Что это такое?? Это соответствует!

Даже если я использую View вместо GetUserView, он не компилируется.

class UserDemoPresenter : Presenter {
    typealias V = View
}

UserDemoPresenter.swift Возможно, предполагаемое совпадение "V" (он же "View") не соответствует "View"

xxDD Я не понимаю, правда.

--UPDATED--

С помощью решения, предложенного Робом Напиером, проблема не устранена, а просто отложена.

При попытке определить ссылку на UserDemoPresenter мне нужно указать универсальный тип, поэтому я получаю ту же ошибку:

private var presenter : UserDemoPresenter<GetUserView>

Использование GetUserView в качестве конкретного типа, соответствующего протоколу GetUserView, не поддерживается.

1 ответ

Решение

Основная причина ограничения заключается в том, что у Swift нет метатипов первого класса. Простейший пример - это не работает:

func isEmpty(xs: Array) -> Bool {
    return xs.count == 0
}

Теоретически, этот код мог бы работать, и если бы он работал, я мог бы создать много других типов (таких как Functor и Monad, которые на самом деле сегодня не могут быть выражены в Swift). Но ты не можешь. Вы должны помочь Swift прибить это к конкретному типу. Часто мы делаем это с помощью дженериков:

func isEmpty<T>(xs: [T]) -> Bool {
    return xs.count == 0
}

Заметить, что T здесь совершенно излишне Нет причин, по которым я должен был бы это выразить; это никогда не используется. Но Свифт требует этого, чтобы он мог превратить абстрактные Array в бетон [T], То же самое верно и в вашем случае.

Это конкретный тип (ну, это абстрактный тип, который будет превращен в конкретный тип каждый раз, когда он создается и P заполнено):

class ImplementProtocolA<P : ProtocolA>

Это полностью абстрактный тип, который Swift не имеет никаких правил для превращения в конкретный тип:

class ImplementProtocolB : ImplementProtocolA<ProtocolB>

Вы должны сделать это конкретным. Это скомпилирует:

class ImplementProtocolB<T: ProtocolB> : ImplementProtocolA<T> {}

А также:

class UserDemoPresenter<T: GetUserView> : Presenter {
    typealias V = T
}

Просто потому, что вы, вероятно, столкнетесь с проблемой позже: ваша жизнь станет намного проще, если вы сделаете эти структуры или final классы. Протоколы смешивания, обобщения и полиморфизм классов полны очень острых граней. Иногда вам везет, и это просто не скомпилируется. Иногда это будет называть вещи, которые вы не ожидаете.

Возможно, вас заинтересует "Маленькое уважение к AnySequence", в котором подробно описаны некоторые связанные с этим вопросы.


private var presenter : UserDemoPresenter<GetUserView>

Это все еще абстрактный тип. Ты имеешь в виду:

final class Something<T: GetUserView> {
    private var presenter: UserDemoPresenter<T>
}

Если это создает проблему, вам нужно создать коробку. Видите, протокол не соответствует самому себе? для обсуждения того, как вы печатаете, так что вы можете хранить абстрактные типы. Но вам нужно работать в конкретных видах. Вы не можете в конечном итоге специализироваться на протоколе. Вы должны в конечном итоге специализироваться на чем-то конкретном в большинстве случаев.

Другие вопросы по тегам