На ковариантный тип «Self» нельзя ссылаться из инициализатора хранимого свойства - PullRequest
1 голос
/ 11 марта 2020

В приведенном ниже коде я не понимаю, почему я получаю указанную ошибку при использовании Self()? Код просто отлично работает, если я заменю его на Fireman().

final class Fireman {

    var numOfServices = 0

    private init(){}
    static var shared = Self() <-------- Here !!!

    func extinguishFire() {
        self.numOfServices += 1
        print("Spraying water...\(self.numOfServices)")
    }
}

. Кроме того, причина, по которой я должен был пометить класс final, заключается в том, что без этого сообщения об ошибке я должен был включить required инициализатор (и когда я делаю это, я снова получаю ошибку, потому что мой инициализатор private). Так что, чтобы избежать дальнейшего разделения на подклассы, я против своей воли объявил класс окончательным

1 Ответ

2 голосов
/ 12 марта 2020

Это очень похоже на Протокол веселья c Возвращение Self , но достаточно отличается, что, вероятно, стоит ответить отдельно. Здесь есть две проблемы.

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

Рассмотрите следующий подкласс:

class LocalFireman: Fireman {
    let zipcode: String
    init(zipcode: String) { self.zipcode = zipcode }
}

Что следует сделать LocalFireman.shared вернуть? Он не может вернуть пожарного. Вы сказали, что он должен вернуть Self (т.е. LocalFireman). И он не может вернуть LocalFireman, так как у него нет почтового индекса для инициализации. Так что же теперь?

Swift не позволяет вам войти в этот угол, требуя от вас либо прибить определенный c тип shared (то есть это всегда будет Fireman, даже если вы вызовите его для подкласса), или вам нужно пообещать, что подклассов не будет, или вам нужно, чтобы все подклассы реализовали init:

required init() {}

ОК, но вы дошли до этого. Вы отметили это final, и он все еще жалуется. Теперь вы попали в ограничение компилятора. Весь смысл Self - ковариация; это тип Dynami c в точке вызова, а не тип STATI c в контексте . Это не просто удобный способ сказать «мой тип», и это был выбор (хотя я думаю, что это может измениться). Это означает «мой ковариантный тип», даже если вы находитесь в ситуации, когда не может быть никаких подтипов.

Все, что сказано, учитывая private init, я запутался, почему вы говорите, отмечая его final "против моей воли". Если все init являются частными, это не может быть разделено на подклассы в любом случае, поэтому кажется, что вам нужен конечный класс, и если это так, то просто поместите имя класса, куда он идет. Self не для этой проблемы (сегодня).

Это оставляет открытым вопрос о том, почему вы не можете сделать это с требуемым init. Self как «тип класса» и Self как «тип объекта, соответствующего протоколу» рассматриваются как одно и то же. Таким образом, вы не можете думать об этом только в классовой ситуации; все может «наследовать» (соответствовать) протоколу. Так что Self потенциально может ссылаться на структуру. Структуры могут быть любого размера. Таким образом, разрешение хранимого свойства типа Self создает проблемы с разметкой памяти. (Я не думаю, что Swift обещает, что классы всегда будут реализованы как указатель, так что это может вызвать ту же проблему для классов, по крайней мере, в принципе.)

Вы можете вернуть значение типа Self из функции, но вы не можете поместить ее в сохраненное свойство (это верно как для свойств stati c и non-stati c), так и Swift также не допускает их для вычисляемых свойств (I Предположим, это только для согласованности). Таким образом, допустимо (и действительно полезно) следующее:

class Fireman {
    required init() {}
    static func make() -> Self { return Self() }
}

И это то, для чего Self.

...