简体   繁体   English

Swift协议要求,只能通过使用最终类来满足

[英]A Swift protocol requirement that can only be satisfied by using a final class

I'm modeling a owner/ownee scheme on Swift: 我在Swift上为owner / ownee方案建模:

class Owner<T: Ownee> {
     // ...
}

protocol Ownee {
    var owner: Owner<Self> { get }
}

Then I have a pair of classes professor/student that adhere to the modeled types above: 然后我有一对班级教授/学生遵守上面的建模类型:

class Professor: Owner<Student> {
    // ...
}

class Student: Ownee {
    let professor: Professor
    var owner: Owner<Student> {  // error here (see below)
        return professor
    }

    init(professor: Professor) {
        self.professor = professor
    }
}

However I get the following error on the definition of var owner in the Student class: 但是,我在Student类的var owner定义中收到以下错误:

Protocol 'Ownee' requirement 'owner' cannot be satisfied by a non-final class ('Student') because it uses 'Self' in a non-parameter, non-result type position 协议'Ownee'要求'所有者'不能被非最终类('学生')满足,因为它在非参数非结果类型位置使用'Self'

I'm trying to understand what's the cause for this error, why making the class Student final would fix it, and if there's some workaround to be able to model this differently, without making this class final. 我试图理解这个错误的原因是什么,为什么让Student最终的类会修复它,如果有一些解决方法能够以不同的方式对其进行建模,而不是让这个类最终。 I've googled about that error, but haven't found much so far. 我已经搜索了这个错误,但到目前为止还没有找到太多。

The Error is correct. 错误是正确的。 You have to make your class final, since no subclasses could conform your protocol Ownee . 你必须让你的课成为最终,因为没有子类可以符合你的协议Ownee

Consider this subclass: 考虑这个子类:

class FirstGradeStudent: Student {
   // inherited from parent
   // var owner: Owner<Student> {
   //     return professor
   //  }
}

As you can see, it would have to implement var owner: Owner<Student> because of his parent, but it should be implementing var owner: Owner<FirstGradeStudent> instead, because the protocol contains var owner: Owner<Self> { get } and in this case Self would be FirstGradeStudent . 正如您所看到的,它必须实现var owner: Owner<Student>因为他的父级,但它应该实现var owner: Owner<FirstGradeStudent> ,因为协议包含var owner: Owner<Self> { get }在这种情况下, Self将成为FirstGradeStudent

Workaround 解决方法

1: Define a superclass to Ownee , it should be used by Owner : 1:Ownee定义一个超类,它应该由Owner

class Owner<T: OwneeSuper> {
    // ...
}

protocol OwneeSuper {}    
protocol Ownee: OwneeSuper {
    associatedtype T: OwneeSuper
    var owner: Owner<T> { get }
}

OwneeSuper is just a workaround to overcome this problem , otherwise we would just be using: OwneeSuper只是解决这个问题的一种解决方法,否则我们只会使用:

protocol Ownee {
    associatedtype T: Ownee
    var owner: Owner<T> { get }
}

2. In classes that conform to Ownee , you must turn the abstract type of the associatedtype into a concrete class by defining a typealias : 2.在符合类Ownee ,你必须把抽象类型的associatedtype通过定义成具体的类typealias

class Student: Ownee {
    typealias T = Student // <<-- define the property to be Owner<Student>
    let professor: Professor
    var owner: Owner<T> { 
        return professor
    }

    init(professor: Professor) {
        self.professor = professor
    }
}

3. Subclasses can now make use of the property, which will be of your defined type: 3.子类现在可以使用属性,该属性将是您定义的类型:

class FirstGradeStudent: Student {
    func checkOwnerType() {
        if self.owner is Owner<Student> { //warning: 'is' test is always true
            print("yeah!")
        }
    }
}

What happens if Student is subclassed? 如果Student是子类,会发生什么? The owner property remains Owner<Student> , but Student != StudentSubclass . 所有者属性仍为Owner<Student> ,但Student != StudentSubclass

By making your Student class conform to the Ownee protocol, you must satisfy the contract of the protocol. 通过使您的Student类符合Ownee协议,您必须满足协议的合同。 The Ownee protocol states that Owner has type constraint, such that the Owner generic type is the type that's conforming to Ownee ( Student , in this case). Ownee协议声明Owner具有类型约束,因此Owner泛型类型是符合Ownee (在这种情况下为Student )的类型。

If the compiler were to allow subclassing (ie by allowing you to not make Student final), then it would be possible for a StudentSubclass to exist. 如果编译器允许子类化(即允许你不让Student终结),那么StudentSubclass可能存在。 Such a subclass would inherit the Owner property, of type Owner<Student> , but Student is not the same as StudentSubclass . 这样的子类将继承Owner<Student>类型的Owner属性,但StudentStudentSubclass The Ownee protocol's contract has been breached, thus, such a subclass cannot be allowed to exist. Ownee协议的合同已被破坏,因此,不允许存在这样的子类。

The following syntax should support what you're after: 以下语法应该支持您所追求的内容:

protocol Ownee {
    associatedtype Owned = Self where Owned:Ownee
    var owner: Owner<Owned> { get }
}

(tested using Swift 4 - Beta 1) (使用Swift 4测试 - Beta 1)

If you are coming to this thread just looking for a way to have subclasses inherit a method referring to its own type (type of Self) one workaround would be to parameterize the parent class on the child's type. 如果你来这个线程只是想找到一种方法让子类继承一个引用它自己的类型(Self类型)的方法,一种解决方法是参数化子类型的父类。 eg 例如

class Foo<T> {
    func configure(_ block: @escaping (T)->Void) { }
}

class Bar: Foo<Bar> { }

Bar().configure { $0... }

BUT... it also seems that you can add a method in a protocol extension that would not be considered to conform in the protocol itself. 但是......似乎您可以在协议扩展中添加一个方法,该方法在协议本身中不会被认为是符合的。 I don't fully understand why this works: 我不完全理解为什么会这样:

protocol Configurable {
    // Cannot include the method in the protocol definition
    //func configure(_ block: @escaping (Self)->Void)
}

public extension Configurable {
    func configure(_ block: @escaping (Self)->Void) { }
}

class Foo: Configurable { }

class Bar: Foo { }

Bar().configure { $0... }

If you uncomment the method in the protocol definition itself you'll get the Protocol 'Configurable' requirement 'configure' cannot be satisfied by a non-final class ('Foo') because it uses 'Self' in a non-parameter, non-result type position compile error. 如果您取消对协议定义本身中的方法的注释,您将获得Protocol 'Configurable' requirement 'configure' cannot be satisfied by a non-final class ('Foo') because it uses 'Self' in a non-parameter, non-result type position编译错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 初始化器要求 'init(json:)' 只能由非最终 class 'UIColor' 定义中的“必需”初始化器满足 - Initializer requirement 'init(json:)' can only be satisfied by a `required` initializer in the definition of non-final class 'UIColor' 协议要求无法满足 - Protocol requirement cannot be satisfied 为什么协议中的 get-only 属性要求不能被符合的属性满足? - Why can't a get-only property requirement in a protocol be satisfied by a property which conforms? UIViewControllerRepresentable - “非最终类不能满足协议” - SwiftUI - UIViewControllerRepresentable - "Protocol cannot be satisfied by a non-final class" - SwiftUI 使Core Data类成为final,以满足协议的“Self”要求 - Making the Core Data class as final to satisfy the protocol 'Self' requirement 具有最终类的多态性,该类在 swift 中实现了关联类型协议 - Polymorphism with a final class that implements an associatedtype protocol in swift Swift类型检查器拒绝将具有类要求的协议作为类类型 - Swift type checker rejects protocol that has a class requirement as a class type 需要通过使用需求的特定子类(或符合它的类型)来满足Swift协议要求 - Need to satisfy Swift protocol requirement by using a specific subclass of the requirement (or type that conforms to it) 在Swift中,Objective-C类的属性是否可以满足Swift @obj协议计算属性要求? - In Swift, is it possible for a Objective-C class' properties to satisfy a Swift @obj protocol computed property requirement? 仅在符合 Class AND 协议时才进行 Swift 扩展 - Swift extension only when conforming to Class AND protocol
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM