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