通用方法覆盖在swift中不起作用

Rah*_*iya 7 generics swift swift2

有一个协议Printable和一个来自第三方的结构打印机.

protocol Printable {}

struct Printer {

    static func print<T>(object: T) -> String {
        return "T"
    }

    static func print<T: Printable>(object: T) -> String {
        return "Printable"
    }

}
Run Code Online (Sandbox Code Playgroud)

现在我正在制作一个通用的

struct Generic<T> {

    var args: T

    func display() {
        print(Printer.print(args))
    }

}
Run Code Online (Sandbox Code Playgroud)

和两个结构

struct Obj {}
struct PrintableObj: Printable {}
var obj = Generic(args: Obj())
var printableObj = Generic(args: PrintableObj())
Run Code Online (Sandbox Code Playgroud)

当我在它们上面调用显示功能时.

obj.display()
Run Code Online (Sandbox Code Playgroud)

显示T.

printableObj.display()
Run Code Online (Sandbox Code Playgroud)

显示T但我希望它打印"可打印"

我能想到的一个解决方案是拥有两种不同的泛型

struct Generic<T>
struct PrintableGeneric<T: Printable>
Run Code Online (Sandbox Code Playgroud)

是否有任何其他解决方案,而无需更改Printable协议和Printer结构.

Rob*_*ier 1

是的。但答案有点奇怪。第一部分很有道理;第二部分非常奇怪。让我们来看看它。

struct Generic<T> {
    var args: T
    func display() {
        print(Printer.print(args))
    }
}
Run Code Online (Sandbox Code Playgroud)

选择正确的重载print是在编译时而不是运行时决定的。这是最让人困惑的事情。他们希望像 JavaScript 一样对待 Swift,一切都是动态的。Swift 喜欢静态,因为这样它可以确保你的类型是正确的,并且可以进行大量优化(并且 Swift喜欢进行编译器优化)。那么,编译时,什么类型args?嗯,是的TT已知是Printable?不它不是。所以它使用非Printable版本。

但是当 Swift 专门Generic使用时PrintableObj,它难道不知道它是 吗Printabledisplay编译器当时不能创建不同的版本吗?是的,如果我们在编译时知道该函数将存在的每个调用者,并且它们都不会被扩展Printable(这可能发生在完全不同的模块中)。如果不创建大量奇怪的极端情况(例如,内部事物的行为与公共事物不同),并且不强迫 Swift 主动生成display未来调用者可能需要的每个可能版本,就很难解决这个问题。Swift 可能会及时改进,但我认为这是一个难题。(Swift 已经遭受了一些性能下降,因此公共泛型可以在不访问原始源代码的情况下进行专门化。这将使该问题变得更加复杂。)

好的,我们明白了。T不是Printable。但是,如果我们有一个在编译时明确知道的类型并且存在于该函数内,怎么办?Printable那样行得通吗?

func display() {
    if let p = args as? Printable {
        print(Printer.print(p))
    } else {
        print(Printer.print(args))
    }
}
Run Code Online (Sandbox Code Playgroud)

哦,如此接近……但不完全是。这几乎有效。实际上,它if-let完全按照您想要的方式执行。p被分配。它是Printable。但它仍然调用非 Printable 函数。?!?!?!?!

我个人认为 Swift 目前存在这个问题,并且非常希望它能够得到修复。它甚至可能是一个错误。问题是Printable本身不符合Printable。是的,我也不明白,但你明白了。因此,我们需要制作一些符合要求的东西才能获得Printable正确的过载。像往常一样,使用橡皮擦来救援。

struct AnyPrintable: Printable {
    let value: Printable
}

struct Generic<T> {
    var args: T

    func display() {
        if let p = args as? Printable {
            print(Printer.print(AnyPrintable(value: p)))
        } else {
            print(Printer.print(args))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这将按照您想要的方式打印。(假设Printable需要一些方法,您只需将这些方法添加到AnyPrintable类型橡皮擦中即可。)

当然,正确的答案是不要在Printer. 它实在是太混乱和脆弱了。看起来很好看,但是总是会爆炸。