是否可以从通用函数返回符合协议A或协议B的对象?

时间:2014-08-08 12:00:29

标签: swift protocols

这样的事情:

protocol A {}
protocol B {}

func newObject<T: A, B>(flag: Bool) -> T {
  if flag {
    var aaa: A
    return aaa
  } else {
    var bbb: B
    return bbb
  }
}

我收到错误:

'A' is not convertible to 'T'

2 个答案:

答案 0 :(得分:5)

您正在寻找的类型称为“Either”,并且可以使用枚举在Swift中轻松创建。例如:

protocol A {}
protocol B {}

enum AorB {
  case IsA(A)
  case IsB(B)
}

func newObject(flag: Bool) -> AorB {
  if flag {
    return .IsA(...create an A...)
  } else {
    return .IsB(...create a B...)
  }
}

在创建这样的东西时,你应该仔细考虑你的类型的真正含义。什么是AorB真的?可以通过创建Either<Left,Right>(在某些语言中常见)来为此创建通用持有者,但在许多情况下,有更好的名称可以提供给此枚举(如Success和{{1 }}或FailureLocal)。当您的类型干净利落时,您的程序更可能是正确的。


请注意,Swift确实支持返回重载。例如:

Remote

但是,这应该谨慎使用,因为对于调用者来说这可能很尴尬(当然,传递一个标志......)。对于可能以类型推理工作的方式链接的函数更有意义。在我看来,如果你有一个在两个行为之间翻转的布尔值,你真正拥有的是两个函数,你应该强烈地倾向于为它们命名不同的东西。


这是一个完整的工作示例。不幸的typealias A = String typealias B = Int func newObject() -> A { return "a" } func newObject() -> B { return 1 } let x:A = newObject() 类型是Beta5的解决方法。编译器尚未实现在枚举中存储任意大小的通用对象的能力。它必须在编译时知道大小。所以我们用Box得到它,它有一个已知的大小,因为它只有一个Box(这对我们来说真的是拳击)。通过Swift的v1,您应该能够完全删除[]包装器。

Box

答案 1 :(得分:0)

您收到上面的错误消息,因为您的返回值仅符合协议A或协议B.但是,您的签名表明返回类型符合两者。

您的签名:

func newObject<T: A, B>(flag: Bool) -> T

可以读作符合A和B的返回类型。

您实际需要的是返回类型重载。从我的理解不存在。但是,您可以通过返回两个选项作为元组来解决这个问题。以下是如何执行此操作的简短示例。

protocol a{}
protocol b{}

class AnAClass : a{
    var name = "A Class"
}

class AnBClass : b{
    var name = "B Class"
}

func newObject(flag: Bool) -> (a?, b?){
    if flag{
        var aaa: a? = AnAClass()
        return (aaa, nil)
    } else {
        var bbb: b? = AnBClass()
        return (nil, bbb)
    }
}

var tuple = newObject(false)
if let anA = tuple.0 {
    println("We have an A")
} else if let anB = tuple.1{
    println("We have a B");
} else {
    println("Nothing");
}

我说实话,我不认为上面看起来很干净。我建议一个替代方案,但我不完全确定你要解决的问题。