Наследование протокола Swift и общие функции

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

import Foundation

protocol StringInitable {
    init( string:String )
}

class A : StringInitable {
    var stored:String

    required init ( string:String ) {
        stored = string
    }
}

class B : A /*, StringInitable */ {
    var another_stored:String

    required init ( string:String ) {
        another_stored = "B-store"

        super.init(string: string)
    }
}

func maker<T:StringInitable>(string:String) -> T {
    return T(string: string)
}

let instanceA = A(string: "test-maker-A")
let instanceB = B(string: "test-maker-B")

let makerA:A = maker("test-maker-A")
let makerB:B = maker("test-maker-B")

let typeInstanceA = _stdlib_getTypeName(instanceA)
let typeMakerA = _stdlib_getTypeName(makerA)

let typeInstanceB = _stdlib_getTypeName(instanceB)
let typeMakerB = _stdlib_getTypeName(makerB)

Судя по результатам, компилятор, похоже, определил правильные типы, но не смог вызвать правильные инициализаторы. Почему я должен явно реализовать StringInitable в B-классе (проверить, удалив комментарий в определении класса B), чтобы общая функция «создатель» вызывала правильный инициализатор?


person Måns Severin    schedule 06.10.2014    source источник
comment
У меня это сработало, как только я добавил необходимый спецификатор перед функцией инициализации.   -  person user965972    schedule 06.06.2015


Ответы (1)


Это пахнет ошибкой компилятора по одной простой причине: makerB является переменной типа B, но ей присваивается экземпляр A. Это не должно быть возможным, и на самом деле, если вы попытаетесь напечатать и, в более общем смысле, получить доступ к свойству another_stored переменной makerB, возникнет исключение времени выполнения, и я бы не ожидал ничего другого.

Это потому, что если B является подклассом A, экземпляр A не может быть присвоен переменной типа B (в то время как обратное возможно).

Присвоение переменной типа A переменной типа B возможно, но только при следующих условиях:

  • выполняется явное понижение от A до B (в противном случае компилятор должен ошибаться)
  • экземпляр, на который ссылается переменная A, на самом деле является экземпляром B (в противном случае должно быть возбуждено исключение времени выполнения)

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

person Antonio    schedule 06.10.2014