只能通过使用最终类

时间:2016-05-10 14:13:04

标签: swift oop generics swift-protocols

我在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 ownerStudent的定义出现以下错误:

  

协议'Ownee'要求'所有者'不能满足   非最终类('学生'),因为它在非参数中使用'Self',   非结果类型位置

我正在尝试了解导致此错误的原因是什么,为什么让类Student最终修复它,以及是否有一些解决方法能够以不同方式对其进行建模,而不会使此类成为最终版本。我已经用Google搜索了这个错误,但到目前为止还没有找到太多。

4 个答案:

答案 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泛型类型符合OwneeStudent的类型,在这种情况下)。

如果编译器允许子类化(即允许你不使Student最终),那么StudentSubclass就有可能存在。这样的子类将继承Owner类型的Owner<Student>属性,但StudentStudentSubclass不同。 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编译错误。