在 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'

如果我将 chassis 设置为“ var”,则会得到错误:

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

我在“重写属性”指南中找到的唯一一件事情就是我们必须重写 getter 和 setter,这可能会改变属性的值(如果它是“ var”) ,但是改变属性类会怎么样呢?

76820 次浏览

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 let newRacingChassis = newValue as? RacingChassis {
racingChassis = newRacingChassis
} else {
println("incorrect chassis type for racecar")
}
}
}
}

似乎不能使用 let 语法声明一个属性,并在其子类中使用 var 覆盖它,反之亦然,这可能是因为超类实现可能不希望该属性在初始化后发生更改。因此,在这种情况下,还需要在超类中使用‘ var’声明属性,以匹配子类(如上面的代码片段所示)。如果不能更改超类中的源代码,那么最好的办法可能是每次底盘需要更改时就销毁当前的 RaceCar 并创建一个新的 RaceCar。

您可以简单地创建 RacingChassis 的另一个变量。

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


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

这个好像有用

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 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 游乐场中非常有效。

但是 ,如果你在一个实际的项目中尝试,编译器错误会告诉你:

声明“视图”不能重写多个超类声明

到目前为止,我只检查了 Xcode 6.0 GM。

不幸的是,你必须等到苹果解决这个问题。

我也提交了一份错误报告

除了必须使用 let 关键字而不是 var 声明超类之外,提供的解决方案破折号工作得很好。这里有一个解决方案,是可能的,但不推荐!

下面的解决方案将使用 Xcode 6.2,SWIFT 1.1(如果所有的类都在不同的快捷文件中)编译,但是应该避免,因为它可能导致意外的行为(包括崩溃,特别是在使用非可选类型时)。 注意: 这不适用于 Xcode6.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
}
}
}

我已经看到了很多原因,为什么用变量而不是函数来设计 API 是有问题的,而且对我来说,使用计算属性感觉像是一种变通方法。有充分的理由将实例变量封装起来。在这里,我创建了一个协议,汽车,汽车符合。此协议有一个返回 Chassis 对象的访问器方法。因为 Car 符合它,所以 RaceCar 子类可以覆盖它并返回一个不同的 Chassis 子类。这允许 Car 类编程到一个接口(Automobile) ,而知道 RacingChassis 的 RaceCar 类可以直接访问 _ 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()
}
}

试试这个:

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

只需用不同的变数命名原则(如 imgview)设置图像视图属性,因为图像视图已经是它自己的属性,我们无法分配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()
}
}

根据您计划如何使用该属性,最简单的方法是为子类使用可选类型,并为 super 重写 didSet {}方法:

class Chassis { }
class RacingChassis: Chassis { }


class Car {
// Declare this an optional type, and do your
// due diligence to check that it's initialized
// where applicable
var chassis: Chassis?
}
class RaceCar: Car {
// The subclass is naturally an optional too
var racingChassis: RacingChassis?
override var chassis: Chassis {
didSet {
// using an optional, we try to set the type
racingChassis = chassis as? RacingChassis
}
}
}

显然,您需要花费一些时间进行检查,以确保类可以以这种方式进行初始化,但是通过将属性设置为可选属性,您可以保护自己免受强制转换不再起作用的情况的影响。

其他答案的微小变化,但更简单,更安全的一些好处。

class Chassis {}
class RacingChassis : Chassis {}


class Car {
let chassis: Chassis


init(chassis: Chassis) {
self.chassis = chassis
}
}
class RaceCar: Car {
var racingChassis: RacingChassis? {
get {
return chassis as? RacingChassis
}
}
}

其好处包括对底盘必须是什么没有限制(var、 let、可选等) ,并且很容易子类化 RacCar。然后,RaceCar 的子类可以拥有自己的底盘(或 racingChassis)计算值。

简单使用泛型,例如。

class Chassis {
required init() {}
}
class RacingChassis : Chassis {}


class Car<ChassisType : Chassis> {
var chassis = ChassisType()
}




let car = Car()
let racingCar = Car<RacingChassis>()
    

let c1 = car.chassis
let c2 = racingCar.chassis
    

print(c1) // Chassis
print(c2) // RacingChassis

此外,Chassis 甚至不需要成为子类:

protocol Chassis {
init()
}
class CarChassis: Chassis{
required init() {
}
}
class RacingChassis : Chassis {
required init() {
}
}


class Car<ChassisType : Chassis> {
var chassis = ChassisType()
}