Переопределение свойства суперкласса с другим типом в 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'


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

636   12  

12 ответов:

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 ' в суперклассе также соответствует подклассу (как показано в приведенном выше фрагменте). Если вы не можете изменить исходный код в суперклассе, то, вероятно, лучше всего уничтожить текущий гоночный автомобиль и создать новый гоночный автомобиль каждый раз, когда шасси должно быть изменено.

Это, кажется, работает

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()

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

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

предоставленное решение Dash работает хорошо, за исключением того, что суперкласс должен быть объявлен с ключевым словом let, а не var. Вот решение, которое возможно, но не рекомендуется!

Решение ниже будет компилироваться с Xcode 6.2, SWIFT 1.1(если все классы находятся в разных файлах swift), но его следует избегать, потому что это может привести к неожиданному поведению (включая сбой, особенно при использовании необязательных типов). ПРИМЕЧАНИЕ: ЭТО НЕ РАБОТАЕТ С 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
        }
    }
}

теоретически, вы можете сделать это таким образом...

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

Я видел много причин, почему проектирование API с использованием переменных вместо функций является проблематичным, и для меня использование вычисленных свойств кажется обходным путем. Есть веские причины, чтобы сохранить ваши переменные экземпляра инкапсулированы. Здесь я создал автомобиль протокола, которому соответствует автомобиль. Этот протокол имеет метод, который возвращает объект шасси. Поскольку автомобиль соответствует ему, подкласс RaceCar может переопределить его и вернуть другой подкласс шасси. В данном классе автомобилей чтобы запрограммировать интерфейс (автомобиль) и класс 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 и удалите переменную mode из расширения):

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
    }
}

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

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()
    }
}

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

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

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% тип безопасный код без силы разворачивает.

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

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())
    }
}

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

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()
    }
}

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

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

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

Comments

    Ничего не найдено.