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
属性,但 Student
与 StudentSubclass
不同。 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
编译错误。
我正在为 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
属性,但 Student
与 StudentSubclass
不同。 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
编译错误。