Swift에서 유형이 다른 Superclass 속성을 재정의하는 중
스위프트에서 누군가가 슈퍼클래스의 속성을 원래 속성에서 하위 분류된 다른 개체로 재정의하는 방법을 설명할 수 있습니까?
다음과 같은 간단한 예를 들어 보겠습니다.
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'인 경우). 하지만 속성 클래스를 변경하는 것은 어떻습니까?
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'로 선언해야 합니다(위의 스니펫 참조).슈퍼 클래스에서 소스 코드를 변경할 수 없다면 섀시를 개조해야 할 때마다 현재 레이스카를 파괴하고 새 레이스카를 만드는 것이 최선일 것입니다.
이것은 효과가 있는 것 같습니다.
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 에서 확인하십시오.
이론적으로는 이런 식으로 할 수 있습니다
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
슈퍼 클래스를 var가 아닌 let 키워드로 선언해야 한다는 점을 제외하고는 제공된 솔루션 대시가 잘 작동합니다.여기 가능하지만 권장되지 않는 솔루션이 있습니다!
아래 솔루션은 Xcode 6.2, SWIFT 1.1(모든 클래스가 서로 다른 스위프트 파일에 있는 경우)로 컴파일되지만 예기치 않은 동작(특히 비옵션 유형을 사용하는 경우 충돌 포함)을 초래할 수 있으므로 피해야 합니다.참고: 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
}
}
}
함수 대신 변수를 사용하여 API를 설계하는 것이 문제가 되는 이유를 많이 보았고 계산된 속성을 사용하는 것이 해결책처럼 느껴졌습니다.인스턴스(instance) 변수를 캡슐화한 상태로 유지하는 데는 좋은 이유가 있습니다.여기서 저는 Car가 준수하는 자동차 프로토콜을 만들었습니다.이 프로토콜에는 섀시 개체를 반환하는 접근자 메서드가 있습니다.차량이 이를 준수하므로 레이스카 하위 클래스가 이를 무시하고 다른 섀시 하위 클래스를 반환할 수 있습니다.이를 통해 Car 클래스는 인터페이스(Automobile)로 프로그래밍할 수 있으며 RacingChassis에 대해 알고 있는 RacingCar 클래스는 _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 설계가 중단되는 또 다른 예는 프로토콜에 변수가 있을 때입니다.저장된 속성을 제외하고 모든 프로토콜 기능을 확장할 수 있는 확장으로 분할하려면 AdaptiveViewController 클래스의 코드 주석을 해제하고 확장에서 모드 변수를 제거해야 합니다.
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 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% 유형의 안전 코드를 얻을 수 있습니다.
하지만 이것은 일종의 해킹이며, 만약 당신이 당신의 클래스 선언보다 몇 가지 속성을 재정의해야 한다면 완전히 엉망으로 보일 것입니다.따라서 이 접근법을 조심해야 합니다.
속성을 사용하는 방법에 따라 가장 간단한 방법은 하위 클래스에 대한 선택적 유형을 사용하고 다음을 재정의하는 것입니다.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 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 {}
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()
}
}
제네릭을 사용하면 간단합니다(예:
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
또한 섀시는 하위 클래스일 필요도 없습니다.
protocol Chassis {
init()
}
class CarChassis: Chassis{
required init() {
}
}
class RacingChassis : Chassis {
required init() {
}
}
class Car<ChassisType : Chassis> {
var chassis = ChassisType()
}
다른 답변의 약간의 변형이지만, 몇 가지 좋은 이점과 함께 더 간단하고 안전합니다.
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, 옵션 등)에 제한이 없으며 레이스카를 쉽게 서브클래스할 수 있다는 장점이 있습니다.그러면 레이스카의 하위 클래스는 섀시(또는 레이싱 섀시)에 대해 자체적으로 계산된 값을 가질 수 있습니다.
단순히 레이싱 섀시의 다른 변수를 만들 수 있습니다.
class Chassis {}
class RacingChassis : Chassis {}
class Car {
let chassis: Chassis
init(){
chassis = Chassis()
}}
class RaceCar: Car {
let raceChassis: RacingChassis
init(){
raceChassis = RacingChassis()
}}
imageView는 이미 자체 속성이기 때문에 imgview와 같은 다른 명명 규칙으로 새 imageview 속성을 설정하면 됩니다. 두 개의 강력한 속성을 할당할 수 없습니다.
언급URL : https://stackoverflow.com/questions/24094158/overriding-superclass-property-with-different-type-in-swift
'programing' 카테고리의 다른 글
| 파일을 세는 bash 명령어가 있습니까? (0) | 2023.05.04 |
|---|---|
| 응답을 피하는 방법.종료() Excel 파일 다운로드 중 "스레드가 중단되었습니다." 예외 (0) | 2023.04.29 |
| ASP에서 최대 업로드 파일 크기를 늘리는 방법.NET? (0) | 2023.04.29 |
| 기존 Excel 값에 선행 0/0을 특정 길이로 추가 (0) | 2023.04.29 |
| Windows 레지스트리에 데이터를 저장해야 하는 시기와 이유는 무엇입니까? (0) | 2023.04.29 |