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

时间:2016-02-16 11:45:38

标签: swift generics swift2

第三方有一个Printable协议和一个struct Printer。

protocol Printable {}

struct Printer {

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

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

}

现在我正在制作通用

struct Generic<T> {

    var args: T

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

}

和两个结构

struct Obj {}
struct PrintableObj: Printable {}
var obj = Generic(args: Obj())
var printableObj = Generic(args: PrintableObj())

当我调用两者上的显示功能时。

obj.display()

显示T

printableObj.display()

显示T但我想要打印&#34;可打印&#34;

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

struct Generic<T>
struct PrintableGeneric<T: Printable>

在没有更改Printable协议和Printer结构的情况下是否还有其他解决方案。

3 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

在我看来,你唯一的选择就是在&#34; print()&#34;中使用if-else和类型转换。功能

static func print<T>(object: T) -> String {
    if let _ = object as? Printable {
        return "Printable"
    }
    return "T"
}

或非通用变体

static func print(object: Any) -> String {
    if let _ = object as? Printable {
        return "Printable"
    }
    return "T"
}

答案 2 :(得分:1)

是。但答案有点奇怪。第一部分有一定的意义;第二部分是完全奇怪的。让我们来看看吧。

struct Generic<T> {
    var args: T
    func display() {
        print(Printer.print(args))
    }
}

print选择正确的重载是在编译时决定的,而不是运行时。这是最让人困惑的事情。他们希望将Swift视为JavaScript,其中一切都是动态的。 Swift喜欢静态,因为它可以确保你的类型是正确的,它可以做很多优化(和Swift 喜欢进行编译器优化)。那么,编译时间,args是什么类型的?好吧,它是T。已知TPrintable?不它不是。所以它使用非{​​{1}}版本。

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

好的,我们明白了。 display不是T。但是如果我们在编译时知道 明确地Printable并且在这个函数内部生活了怎么办?它会起作用吗?

Printable

哦,这么近......但并不完全。这几乎有效。 func display() { if let p = args as? Printable { print(Printer.print(p)) } else { print(Printer.print(args)) } } 实际上完全符合您的要求。 if-let已分配。它是p。但它仍然调用非Printable函数。 ?!?!?!?!

这是一个我个人认为Swift目前正在破产的地方,并且寄予厚望并将寄予厚望。它甚至可能是一个错误。问题是Printable本身不符合Printable。是的,我也没有得到它,但你去了。因此,我们需要制作 符合Printable的内容,以便获得正确的重载。像往常一样,type erasers来救援。

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)) } } } 需要某些方法,您只需将这些方法添加到Printable类型的橡皮擦中。)

当然,正确的答案是不要在AnyPrintable中以这种方式使用泛型重载。这太令人困惑和脆弱了。它看起来很漂亮,但它一直在爆炸。