Подтвердить что ты не робот

Быстрые дженерики, не сохраняющие тип

Я пытаюсь создать и/или создать переменную, основанную на указанном родовом типе. Я понимаю, что стирание стилей не выполняется быстро, но не похоже, что дженерики сохраняют тип, отличный от заданных условий общего типа, например. в соответствии с базовым классом. Кажется, что все, что я могу сделать или инициализировать, - это базовый класс. Что еще более странно, когда я в отладчике, у генерала появляется RawPointer для правильного класса, и даже переменные выглядят так, как будто они имеют правильный тип:

EDIT:

Как и в Xcode 6.1, это все еще проблема (упрощенный код предоставлен Грегори Хигли):

class BaseClass {
    func printme() -> Void {
        println("I am BaseClass")
    }
}

class DerivedClass : BaseClass {
    override func printme() -> Void {
        println("I am DerivedClass")
    }
}

class Util<T: BaseClass> {
    func doSomething() {
        var instance = T()
        instance.printme()
    }
}

var util = Util<DerivedClass>()
util.doSomething()

Печатает "I'm BaseClass"

Также хотелось бы отметить, что требуемый init {} в базовом классе больше не работает.

enter image description here

4b9b3361

Ответ 1

Этот код работает так, как ожидалось.

class BaseClass {

    required init() {} // <-- ADDED THIS

    func printme() -> Void {
        println("I am BaseClass")
    }
}

class DerivedClass : BaseClass {
    override func printme() -> Void {
        println("I am DerivedClass")
    }
}

class Util<T: BaseClass> {
    func doSomething() {
        var instance = T()
        instance.printme()
    }
}

var util = Util<DerivedClass>()
util.doSomething()

База кода украдена из ответа @GregoryHigley:)

Маркировка init() {} как required сделала вещь. Это гарантирует, что init() является назначенным инициализатором производного класса ANY из BaseClass.

Без него можно сделать незаконный подкласс вроде:

class IllegalDerivedClass : BaseClass {
    var name:String

    init(name:String) {
        self.name = name
        super.init()
    }

    override func printme() -> Void {
        println("I am DerivedClass")
    }
}

var util = Util<IllegalDerivedClass>()
util.doSomething()

Вы знаете, что это не работает, потому что IllegalDerivedClass не наследует init() initializer.

Я думаю, что это причина вашей проблемы.

В любом случае, чья ошибка в том, что?

  • Компилятор должен предупреждать о двусмысленности.
  • Время выполнения должно пытаться инициализировать DerivedClass(), как указано в T.
  • Отладчик должен показать instance - это экземпляр BaseClass, как он есть на самом деле.

ДОБАВЛЕНО:

Как и в Xcode 6.1 GM 2, кажется, вам нужно больше работы. (в дополнение к required init() {})

class Util<T: BaseClass> {
    let theClass = T.self // store type itself to variable

    func doSomething() {
        var instance = theClass() // then initialize
        instance.printme()
    }
}

Я совершенно не знаю, зачем нам это нужно, что происходит на X (

ДОБАВЛЕНО: 2014/10/18

Я нашел, что это также работает:

    func doSomething() {
        var instance = (T.self as T.Type)()
        instance.printme()
    }

ДОБАВЛЕНО: 2015/02/10

В Xcode версии 6.3 (6D520o)/Swift 1.2

Нам больше не нужно (T.self as T.Type)() взломать. Просто T() работает до тех пор, пока T имеет required init() инициализатор.

class Util<T: BaseClass> {
    func doSomething() {
        var instance = T()
        instance.printme()
    }
}

Ответ 2

Я создал упрощенную версию вашего кода следующим образом:

class BaseClass {
    func printme() -> Void {
        println("I am BaseClass")
    }
}

class DerivedClass : BaseClass {
    override func printme() -> Void {
        println("I am DerivedClass")
    }
}

class Util<T: BaseClass> {
    func doSomething() {
        var instance = T()
        instance.printme()
    }
}

var util = Util<DerivedClass>()
util.doSomething()

Это переводит проблему в ее суть. Можно было бы ожидать, что util.doSomething() будет печатать "I'm DerivedClass", но каждый раз он печатает "I'm BaseClass". Это должно быть ошибкой, потому что никакая система рационального типа не будет работать таким образом.

Я думаю, вы должны записать это с Apple как ошибка.

Ответ 3

Проблема: var instance = T() Инициализаторы не являются виртуальными, поэтому экземпляр всегда выполняется с помощью BaseClass() *. Следующий код использует функцию класса для решения этой проблемы:

class BaseClass {
    func printme() -> String {
        return "I am BaseClass"
    }
    class func makeInstance() -> BaseClass
    {
        return BaseClass()
    }
}

class DerivedClass : BaseClass {
    override class func makeInstance() -> BaseClass
    {
        return DerivedClass()
    }

    override func printme() -> String {
        return "I am DerivedClass"
    }
}

class Util<T: BaseClass> {
    func doSomething() -> String {
        var instance = T.makeInstance()
        return instance.printme()
    }
}

var util = Util<DerivedClass>()
println("\(util.doSomething())")

Я изменил реализацию printme() только потому, что по какой-то причине исходный код ничего не печатал на игровой площадке.

* Я думаю, что это все еще ошибка.

Ответ 4

У меня была аналогичная проблема. Вам нужно добавить инициализатор required и let realType = T.self и заменить T() на realType().

class BaseClass {
    required init() {}
    func printme() -> Void {
        println("I am BaseClass")
    }
}

class DerivedClass : BaseClass {
    override func printme() -> Void {
        println("I am DerivedClass")
    }
}

class Util<T: BaseClass> {
    func doSomething() {
        let realType = T.self // that it
        var instance = realType()
        instance.printme()
    }
}

var util = Util<DerivedClass>()
util.doSomething()