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

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

我正在为 owner/ownee 方案建模 Swift:

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

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

然后我有一对 classes professor/student 遵循上面的模型类型:

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

但是我在 Student class 中定义 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

我试图了解此错误的原因是什么,为什么将 class Student 设置为 final 会修复它,如果有一些解决方法能够以不同的方式对此进行建模,而无需使这个 class 成为最终版本。我用谷歌搜索了那个错误,但到目前为止还没有找到太多。

错误是正确的。你必须让你的 class 成为最终版本,因为没有子 class 可以符合你的协议 Ownee.

考虑这个子class:

class FirstGradeStudent: Student {
   // Student contains following variable:
   // var owner: Owner<Student> {
   //     return professor
   //  }
}

如您所见,由于他的parent,它必须实现var owner: Owner<Student>,但它应该实现var owner: Owner<FirstGradeStudent>,因为协议包含var owner: Owner<Self> { get }在这种情况下 Self 将是 FirstGradeStudent.

解决方法

1:定义一个superclass到Ownee,它应该被Owner:

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

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

OwneeSuper 只是克服 this problem 的一种变通方法,否则我们只会使用:

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

2.在符合Ownee的classes中,必须把associatedtype的抽象类型变成具体的class 通过定义 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 现在可以使用 属性,它将是您定义的类型:

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

如果 Student 被子class 会怎样?所有者 属性 仍然是 Owner<Student>,但是 Student != StudentSubclass.

通过使您的 Student class 符合 Ownee 协议,您必须满足协议的约定。 Ownee 协议声明 Owner 具有类型约束,因此 Owner 泛型类型是符合 Ownee 的类型(在本例中为 Student) .

如果编译器允许 subclassing(即允许您不使 Student 最终化),那么 StudentSubclass 可能存在。这样的子 class 将继承 Owner<Student> 类型的 Owner 属性,但 StudentStudentSubclass 不同。 Ownee 协议的契约已经被破坏,因此,这样的子class 不能存在。

以下语法应该支持您所追求的:

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

(使用 Swift 4 - Beta 1 测试)

如果你来到这个线程只是为了寻找一种方法让 subclasses 继承一个引用它自己的类型(Self 的类型)的方法,一个解决方法是参数化 parent class child 的类型。例如

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

class Bar: Foo<Bar> { }

Bar().configure { [=10=]... }

但是......您似乎还可以在协议扩展中添加一个方法,该方法不会被认为符合协议本身。我不完全明白为什么会这样:

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 { [=11=]... }

如果您取消注释协议定义本身中的方法,您将得到 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 编译错误。