Swiftで異なるタイプのスーパークラスプロパティをオーバーライドする


129

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'の場合)が、プロパティクラスの変更についてはどうでしょうか。 ?

回答:


115

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を作成するのがおそらく最善です。


1
申し訳ありませんが、私のコメントを削除してから返信を確認しました。私の実際のケースでは、スーパークラスがstrongプロパティを持つObjective-Cクラスであり、それをオーバーライドしようとしてエラーが発生したため、問題が発生chassis!しました。スウィフト、それをoverride var chassis : Chassis!修正します。
James

4
これは、Xcode 6.1のSwift 1.1では動作しなくなりました。「不変の 'let'プロパティ 'chassis'を 'var'のゲッターでオーバーライドできません」というエラーを生成します。より良い解決策のためのアイデアはありますか?
Darrarski、2014年

1
また、Xcode 6.3ベータ版のSwift 1.2では動作しなくなりました。タイプ 'Type1'の可変プロパティ 'A'を共変タイプ 'Type2'でオーバーライドできません
bubuxu

10
これは本当に不自由で、Swiftの失敗です、imo。RacingChassis シャーシであるため、コンパイラーは問題なく、サブクラスのプロパティのクラスを調整できます。多くの言語がこれを可能にし、それをサポートしないことは、このような醜い回避策につながります。攻撃はありません。:/
devios1 2016

1
オプションは、期待される値を返さない関数によって提供されたため、変数が存在しない可能性があることを示唆する手段を提供します。このようなケースは、関数がオーバーライドされた場合に発生する可能性があります。詳細については私の答えを見てください。

10

これはうまくいくようです

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

1
これは、シャーシプロパティがクラスで定義されletているように機能し、Car変更を不可能にします。クラスのchassisプロパティのみ変更できRaceCarます。
David Arve 14年

4
これはSwift 2.0では機能しません。「エラー:不変の 'let'プロパティ 'chassis'を 'var'のゲッターでオーバーライドできません」
mohamede1945

これはSwift 4.0でも機能しません。laygroundの実行に失敗しました:エラー:MyPlaygrounds.playground:14:15:エラー:不変の「let」プロパティ「chassis」を「var」オーバーライドvar chassisのゲッターでオーバーライドすることはできません:RacingChassis {^ MyPlaygrounds.playground:11:6:note :ここでプロパティをオーバーライドしようとしますlet chassis = Chassis()^
karim

7

これを試して:

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の詳細


きちんとしたクリーン
Inder Kumar Rathore 2017

3

提供されているSolution Dashは、スーパークラスをvarではなくletキーワードで宣言する必要があることを除いて、うまく機能します。これは可能な解決策ですが、お勧めしません!

以下の解決策はXcode 6.2、SWIFT 1.1(すべてのクラスが異なるswiftファイルにある場合)でコンパイルされますが、予期しない動作を引き起こす可能性があるため(特に非オプションのタイプを使用する場合はクラッシュを含む)、回避する必要があります。注:これはXCODE 6.3ベータ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
        }
    }
}

1
Swift 2.0では機能しません。これは、「タイプ「シャーシ」の変更可能なプロパティ「シャーシ」をオーバーライドできませんか?」共変タイプ 'RacingChassis?'を使用して
mohamede1945

2

理論的には、この方法で許可されています...

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のみをチェックしています。

残念ながら、Appleがこれを修正するまで待つ必要があります。

バグレポートも提出しました。18518795


これは興味深い解決策であり、6.1で機能します。
Chris Conover、2014年

1

関数の代わりに変数を使用してAPIを設計することが問題であり、計算されたプロパティを使用することが回避策のように思える多くの理由を見てきました。インスタンス変数をカプセル化しておくことには十分な理由があります。ここでは、Carが準拠するプロトコルAutomobileを作成しました。このプロトコルには、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を設計すると失敗するもう1つの例は、プロトコルに変数がある場合です。すべてのプロトコル関数を拡張機能に分割したい場合は、保存されたプロパティを拡張機能に配置できず、クラスで定義する必要があります(これをコンパイルするには、コードのコメントを外す必要があります) 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
    }
}

1

あなたはジェネリックを使用してそれを達成することができます:

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%タイプセーフなコードが得られます。

しかし、これは一種のハックであり、クラス宣言よりもいくつかのプロパティを再定義する必要がある場合、宣言は完全に混乱したように見えます。したがって、このアプローチには注意してください。


1

プロパティの使用計画に応じて、これを行う最も簡単な方法は、サブクラスにオプションの型を使用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
        }
    }
}

当然、クラスをこのように初期化できるようにチェックに時間をかける必要がありますが、プロパティをオプションに設定することで、キャストが機能しなくなった状況から身を守ることができます。


0

RacingChassisの別の変数を作成するだけです。

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

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

これは機能しますが、Dashの答えは、プロパティを使用しchassisRacingChassisインスタンスをオーバーライドおよび取得/設定できるようにすることで、これをさらに一歩進めたと思いますchassis
James

0

これを試して:

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

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

0

次の例では、単一のオブジェクトを基本クラスと派生クラスで使用できます。派生クラスでは、派生オブジェクトプロパティを使用します。

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

0

他の回答のわずかなバリエーションですが、シンプルで安全で、いくつかの優れた利点があります。

class Chassis {}
class RacingChassis : Chassis {}

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

利点としては、シャーシの種類(var、let、optionalなど)に制限がなく、RaceCarをサブクラス化するのが簡単です。RaceCarのサブクラスは、シャーシ(またはRacingChassis)に対して独自の計算値を持つことができます。


-2

imageViewはすでに独自のプロパティであり、2つの強力なプロパティを割り当てることができないため、imgviewのような異なる命名規則で新しいimageviewプロパティを設定するだけです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.