我在Swift上为所有者/ ownee方案建模:
class Owner<T: Ownee> {
// ...
}
protocol Ownee {
var owner: Owner<Self> { get }
}
然后我有一对坚持上述建模类型的班级教授/学生:
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
}
}
但是,var owner
类Student
的定义出现以下错误:
协议'Ownee'要求'所有者'不能满足 非最终类('学生'),因为它在非参数中使用'Self', 非结果类型位置
我正在尝试了解导致此错误的原因是什么,为什么让类Student
最终修复它,以及是否有一些解决方法能够以不同方式对其进行建模,而不会使此类成为最终版本。我已经用Google搜索了这个错误,但到目前为止还没有找到太多。
答案 0 :(得分:13)
错误是正确的。你必须让你的课成为最终,因为没有子类可以符合你的协议Ownee
。
考虑这个子类:
class FirstGradeStudent: Student {
// inherited from parent
// var owner: Owner<Student> {
// return professor
// }
}
正如您所看到的,由于他的父级,它必须实现var owner: Owner<Student>
,但它应该实现var owner: Owner<FirstGradeStudent>
,因为协议包含var owner: Owner<Self> { get }
,在这种情况下Self
1}}将是FirstGradeStudent
。
解决方法强>
1:将超类定义为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
的类中,您必须通过定义associatedtype
来将typealias
的抽象类型转换为具体类: / p>
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。子类现在可以使用属性,该属性将是您定义的类型:
class FirstGradeStudent: Student {
func checkOwnerType() {
if self.owner is Owner<Student> { //warning: 'is' test is always true
print("yeah!")
}
}
}
答案 1 :(得分:5)
如果Student
是子类,会发生什么?所有者属性仍为Owner<Student>
,但Student != StudentSubclass
。
通过使Student
类符合Ownee
协议,您必须满足协议的约定。 Ownee
协议声明Owner
具有类型约束,因此Owner
泛型类型符合Ownee
(Student
的类型,在这种情况下)。
如果编译器允许子类化(即允许你不使Student
最终),那么StudentSubclass
就有可能存在。这样的子类将继承Owner
类型的Owner<Student>
属性,但Student
与StudentSubclass
不同。 Ownee
协议的合同已被违反,因此,不允许存在这样的子类。
答案 2 :(得分:4)
以下语法应该支持您所追求的内容:
protocol Ownee {
associatedtype Owned = Self where Owned:Ownee
var owner: Owner<Owned> { get }
}
(使用Swift 4测试 - Beta 1)
答案 3 :(得分:2)
如果你来这个线程只是想找到一种方法让子类继承一个引用它自己的类型(Self类型)的方法,一种解决方法是参数化子类型的父类。 e.g。
class Foo<T> {
func configure(_ block: @escaping (T)->Void) { }
}
class Bar: Foo<Bar> { }
Bar().configure { $0... }
但是......似乎你可以在一个协议扩展中添加一个方法,该方法在协议本身中不被认为是符合的。我不完全理解为什么会这样:
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... }
如果取消对协议定义本身中的方法的注释,您将收到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
编译错误。