Переопределение свойства суперкласса с разным типом в Swift

В Swift кто-нибудь может объяснить, как переопределить свойство суперкласса с другим объектом, находящимся в подклассе из исходного свойства?

Возьмем этот простой пример:

class Chassis {}
class RacingChassis : Chassis {}

class Car {
    let chassis = Chassis()
}
class RaceCar: Car {
    override let chassis = RacingChassis() //Error here
}

Это дает ошибку:

Cannot override with a stored property 'chassis'

Если у меня есть шасси как "var", я получаю сообщение об ошибке:

Cannot override mutable property 'chassis' of type 'Chassis' with covariant type 'RacingChassis'

Единственное, что я могу найти в руководстве в разделе "Переопределение свойств", означает, что мы должны переопределить getter и setter, которые могут работать для изменения значения свойства (если это "var" ), но что об изменении класс свойств?

Ответ 1

Swift не позволяет вам изменять тип класса каких-либо переменных или свойств. Вместо этого вы можете создать дополнительную переменную в подклассе, которая обрабатывает новый тип класса:

class Chassis {}
class RacingChassis : Chassis {}

class Car {
    var chassis = Chassis()
}
class RaceCar: Car {
    var racingChassis = RacingChassis()
    override var chassis: Chassis {
        get {
            return racingChassis
        }
        set {
            if newValue is RacingChassis {
                racingChassis = newValue as RacingChassis
            } else {
                println("incorrect chassis type for racecar")
            }
        }
    }
}

Кажется, нельзя объявить свойство с синтаксисом let и переопределить его с помощью var в его подклассе или наоборот, что может быть связано с тем, что реализация суперкласса может не ожидать изменения этого свойства после инициализации. Таким образом, в этом случае свойство должно быть объявлено с помощью 'var в суперклассе, чтобы соответствовать подклассу (как показано во фрагменте выше). Если вы не можете изменить исходный код в суперклассе, то, вероятно, лучше уничтожить текущий RaceCar и создать новый RaceCar каждый раз, когда необходимо изменить шасси.

Ответ 2

Это похоже на работу

class Chassis {
    func description() -> String {
        return "Chassis"
    }
}
class RacingChassis : Chassis {
    override func description() -> String {
        return "Racing Chassis"
    }

    func racingChassisMethod() -> String {
        return "Wrooom"
    }
}

class Car {
    let chassis = Chassis()
}
class RaceCar: Car {
    override var chassis: RacingChassis {
    get {
        return self.chassis
    }
    set {
        self.chassis = newValue
    }
    }
}

var car = Car()
car.chassis.description()

var raceCar = RaceCar()
raceCar.chassis.description()
raceCar.chassis.racingChassisMethod()

Ответ 3

Попробуй это:

class Chassis{
     var chassis{
         return "chassis"
     } 
}

class RacingChassis:Chassis{
     var racing{
         return "racing"
     } 
}

class Car<Type:Chassis> {
     let chassis: Type
     init(chassis:Type){
        self.chassis = chassis
     }
}

class RaceCar: Car<RacingChassis> {
     var description{
         return self.chassis.racing
     } 
}

Затем:

let racingChassis = RacingChassis()
let raceCar = RaceCar(chassis:racingChassis)
print(raceCar.description) //output:racing

Подробно в http://www.mylonly.com/14957025459875.html

Ответ 4

Решение Dash предоставлено хорошо работает, за исключением того, что суперкласс должен быть объявлен с ключевым словом let, а не var. Вот решение, которое возможно, но НЕ РЕКОМЕНДУЕТСЯ!

Нижеприведенное решение будет скомпилировано с Xcode 6.2, SWIFT 1.1 (если все классы находятся в разных быстрых файлах), но их следует избегать, потому что ИТ МОЖЕТ ПРИВЕСТИ К НЕОПРЕДЕЛЕННЫМ ПОВЕДЕНИЯМ (ВКЛЮЧАЯ СБРОС, особенно при использовании необязательных типов). ПРИМЕЧАНИЕ: ЭТО НЕ РАБОТАЕТ С XCODE 6.3 BETA 3, SWIFT 1.2

class Chassis {}
class RacingChassis : Chassis {}
class Car {
    var chassis:Chassis? = Chassis()
}

class RaceCar: Car {
    override var chassis: RacingChassis? {
        get {
            return super.chassis as? RacingChassis
        }
        set {
            super.chassis = newValue
        }
    }
}

Ответ 5

Теоретически, вам разрешено делать это таким образом...

class ViewController {

    var view: UIView! { return _view }

    private var _view: UIView!
}

class ScrollView : UIView {}

class ScrollViewController : ViewController {

    override var view: ScrollView! { return super.view as ScrollView! }
}

class HomeView : ScrollView {}

class HomeViewController : ScrollViewController {

    override var view: HomeView! { return super.view as HomeView! }
}

Это отлично работает на игровой площадке Xcode.

Но, если вы попробуете это в реальном проекте, ошибка компилятора сообщает вам:

Объявление 'view' не может отменять более одного объявления суперкласса

Я только проверял Xcode 6.0 GM на данный момент.

К сожалению, вам придется подождать, пока Apple не исправит это.

Я также представил отчет об ошибке. 18518795

Ответ 6

Я видел множество причин, по которым проектирование API с использованием переменных вместо функций проблематично, и для меня использование вычисленных свойств похоже на обходной путь. Имеются веские причины для инкапсуляции ваших переменных экземпляра. Здесь я создал протокол Automobile, который соответствует Car. Этот протокол имеет метод доступа, который возвращает объект шасси. Поскольку Car соответствует ему, подкласс RaceCar может переопределить его и вернуть другой подкласс Chassis. Это позволяет классу Car программировать интерфейс (Automobile), а класс RaceCar, который знает о RacingChassis, может напрямую обращаться к переменной _racingChassis.

class Chassis {}
class RacingChassis: Chassis {}

protocol Automobile {
    func chassis() -> Chassis
}

class Car: Automobile {
    private var _chassis: Chassis

    init () {
        _chassis = Chassis()
    }

    func chassis() -> Chassis {
        return _chassis
    }
}

class RaceCar: Car {
    private var _racingChassis: RacingChassis

    override init () {
        _racingChassis = RacingChassis()
        super.init()
    }

    override func chassis() -> Chassis {
        return _racingChassis
    }
}

Еще один пример того, почему проектирование API с использованием переменных разбивается, - это когда у вас есть переменные в протоколе. Если вы хотите разбить все функции протокола на расширения, вы можете, за исключением того, что хранимые свойства не могут быть размещены в расширениях и должны быть определены в классе (чтобы получить это для компиляции, вам придется раскомментировать код в Класс AdaptableViewController и удалить переменную режима из расширения):

protocol Adaptable {
    var mode: Int { get set }
    func adapt()
}

class AdaptableViewController: UIViewController {
    // var mode = 0
}

extension AdaptableViewController: Adaptable {

    var mode = 0 // compiler error

    func adapt() {
        //TODO: add adapt code
    }
}

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

protocol Adaptable {
    func mode() -> Int
    func adapt()
}

class AdaptableViewController: UIViewController {
}

extension AdaptableViewController: Adaptable {
    func mode() -> Int {
        return 0
    }
    func adapt() {
        // adapt code
    }
}

Ответ 7

Попробуйте следующее:

class Chassis {}
class RacingChassis : Chassis {}
class SuperChassis : RacingChassis {}

class Car {
    private var chassis: Chassis? = nil
    func getChassis() -> Chassis? {
        return chassis
    }

    func setChassis(chassis: Chassis) {
        self.chassis = chassis
    }
}

class RaceCar: Car {
    private var chassis: RacingChassis {
        get {
            return getChassis() as! RacingChassis
        }
        set {
            setChassis(chassis: newValue)
        }
    }

    override init() {
        super.init()

        chassis = RacingChassis()
    }
}

class SuperCar: RaceCar {
    private var chassis: SuperChassis {
        get {
            return getChassis() as! SuperChassis
        }
        set {
            setChassis(chassis: newValue)
        }
    }

    override init() {
        super.init()

        chassis = SuperChassis()
    }
}

Ответ 8

просто установите новое свойство imageview с другим соглашением об именах, например imgview, потому что imageView уже является его собственным свойством, и мы не можем назначить 2 сильных свойства.

Ответ 9

Вы можете достичь этого с помощью дженериков:

class Descriptor {
    let var1 = "a"
}

class OtherDescriptor: Descriptor {
    let var2 = "b"
}

class Asset<D: Descriptor> {
    let descriptor: D

    init(withDescriptor descriptor: D) {
        self.descriptor = descriptor
    }

    func printInfo() {
        print(descriptor.var1)
    }
}

class OtherAsset<D: OtherDescriptor>: Asset<D> {
    override func printInfo() {
        print(descriptor.var1, descriptor.var2)
    }
}

let asset = Asset(withDescriptor: Descriptor())
asset.printInfo() // a

let otherAsset = OtherAsset(withDescriptor: OtherDescriptor())
otherAsset.printInfo() // a b

При таком подходе у вас будет 100% безопасный тип кода без принудительного развертывания.

Но это что-то вроде хака, и если вам нужно будет переопределить несколько свойств, то объявления ваших классов будут выглядеть как полный беспорядок. Так что будьте осторожны с этим подходом.

Ответ 10

class Chassis {}
class RacingChassis : Chassis {}

class Car {
    fileprivate let theChassis: Chassis
    var chassis: Chassis {
        get {
            return theChassis
        }
    }
    fileprivate init(_ chassis: Chassis) {
        theChassis = chassis
    }
    convenience init() {
        self.init(Chassis())
    }
}
class RaceCar: Car {
    override var chassis: RacingChassis {
        get {
            return theChassis as! RacingChassis
        }
    }
    init() {
        super.init(RacingChassis())
    }
}

Ответ 11

Следующее позволяет использовать один объект в базовых и производных классах. В производном классе используйте свойство производного объекта.

class Car {
    var chassis:Chassis?

    func inspect() {
        chassis?.checkForRust()
    }
}

class RaceCar: Car {
    var racingChassis: RacingChassis? {
        get {
            return chassis as? RacingChassis
        } 
    }

    override func inspect() {
        super.inspect()
        racingChassis?.tuneSuspension()
    }
}

Ответ 12

Вы можете просто создать другую переменную RacingChassis.

class Chassis {}
class RacingChassis : Chassis {}
class Car {
    let chassis: Chassis
    init(){
        chassis = Chassis()
}}

class RaceCar: Car {
let raceChassis: RacingChassis
init(){
        raceChassis = RacingChassis()
}}