是否有可能满足Swift协议并添加默认参数?

时间:2016-01-04 23:48:32

标签: swift swift2

如果你有这样的协议:

protocol Messaging {
    func sendMessage(message: String)
}

有没有办法在类似的类中满足它:

class Messager: Messaging {
    func sendMessage(message: String, count: Int = 1) {}
}

这将是很好的,因为通过添加默认参数来满足协议的结果签名。有没有办法让它与Swift 2一起使用?

这是一个简化的例子。让我们说,为了论证,协议是固定的。解决方案只能更新Messager类。我的目标是能够像这样致电sendMessage()

let m: Messaging = Messager()
m.sendMessage("")

我发现完成此任务(并满足编译器)的唯一方法是重载,如下所示:

class Messager: Messaging {
    func sendMessage(message: String) {
        self.sendMessage(message, count: 1)
    }

    func sendMessage(message: String, count: Int = 1) {}
}

这种方法的问题是我的默认值然后在两个地方指定,我失去了Swift默认参数的主要优点。

4 个答案:

答案 0 :(得分:45)

Swift 3 中你可以使用扩展来解决这个问题,但它有点难看。希望在下一个快速版本中提供更好的解决方案。

import UIKit

protocol TestProtocol {
    func testFunction(a:Int, b:Int?) -> String
}

extension TestProtocol
{
    func testFunction(a:Int, b:Int? = nil) -> String {
        return testFunction(a:a, b:b)
    }
}

class TestClass: TestProtocol
{
    func testFunction(a:Int, b:Int?) -> String {
        return "a:\(a), b:\(b)"
    }
}

func testit(testProtocol: TestProtocol) {
    print(testProtocol.testFunction(a:10)) // will print a:10, b:nil
    print(testProtocol.testFunction(a:10, b:20)) // will print a:10, b:Optional(20)
}

let t = TestClass()
testit(testProtocol: t)

答案 1 :(得分:12)

使用Swift 2,您现在可以像这样扩展协议并为其提供默认实现

protocol Messageable {
    func sendMessage(message: String)
}

extension Messageable {
    func sendMessage(message: String, count: Int = 1) {
        // Do your default implementation
    }
}

我还在了解这一点,所以我不能100%确定这对你的发送消息示例有什么用处,但我相信这就是你要找的东西。

你可以用Swift 2中的协议做很多新的酷事。

观看Apple的演示非常好:

https://developer.apple.com/videos/play/wwdc2015-408/

并阅读以下内容:

http://matthijshollemans.com/2015/07/22/mixins-and-traits-in-swift-2/

http://code.tutsplus.com/tutorials/protocol-oriented-programming-in-swift-2--cms-24979

http://www.raywenderlich.com/109156/introducing-protocol-oriented-programming-in-swift-2

答案 2 :(得分:12)

如果有人还在寻找答案,这个链接帮助了我:

https://oleb.net/blog/2016/05/default-arguments-in-protocols/

基本上,原始功能定义包括您需要的所有参数:

protocol Messaging {
    func sendMessage(message: String, count: Int)
}

并且您的扩展程序提供默认值,使用这些默认值调用原始协议函数:

extension Messaging {
    func sendMessage(message: String, count: Int = 1) {
        sendMessage(message, count)
    }
}

答案 3 :(得分:4)

您可以在协议中添加typealiases,以在协议蓝图函数.sendMessage中表示可能的不同参数类型。在下面的示例中,我明确指出第二个参数既没有内部名称也没有外部名称。

由于你有两个typealases,你可以使用两种不同的类型(下面的示例中为Messenger)将此蓝图实现为一个,或者,只需丢弃第二个参数(示例中为AnotherMessenger)as一个空元组类型(),默认值为()(您可以将其视为void类型,其值为void)。

protocol Messaging {
    typealias T
    typealias U
    func sendMessage(message: T, _ _ : U)
}

/* Class where you make use of 2nd argument */
class Messager: Messaging {

    func sendMessage(message: String, _ count: Int) {
        print(message + "\(count)")
    }
}

/* Class where you ignore 2nd argument */
class AnotherMessager : Messaging {

    func sendMessage(message: String, _ _ : () = ()) {
        print(message)
    }
}

/* Tests */
var a = Messager()
a.sendMessage("Hello world #", 1)
// prints "Hello World #1"

var b = AnotherMessager()
b.sendMessage("Hello world")
// prints "Hello World"

这就像我可以让你模拟你在问题和下面的评论中描述的行为一样接近。我将在下面留下替代解决方案,以防他们能够帮助其他有类似想法但设计约束较少的人。

另一种选择:不完全是您要求的行为,但您可以使用匿名闭包作为协议蓝图函数的单个参数,其中此关闭不带参数但返回Any个对象的数组,您可以根据需要在sendMessage功能中访问和处理。

protocol Messaging {
    func sendMessage(@autoclosure messages: ()->[Any])
}

class Messager: Messaging {
    func sendMessage(@autoclosure messages: ()->[Any]) {
        for message in messages() {
            print(message, terminator: "")
        }
    }
}

var a = Messager()
a.sendMessage([String("Hello "), String("World "), String("Number "), Int(1)])
// prints "Hello World Number 1"

另一种选择是必须在协议sendMessage(..)中分隔功能Messaging的蓝图,一个带有一个,而另一个没有附加参数count。此后,您通过扩展协议Messaging为这些函数的两者添加默认(虚拟)实现。您的班级Messager将遵守Messaging协议,即使其中根本没有sendMessage(..)的任何实施;在缺少的情况下,使用默认实现。最后,只详细实现您希望在班级中使用的sendMessage函数。

protocol Messaging {
    func sendMessage(message: String)
    func sendMessage(message: String, count: Int)
}

/* Extend blueprints with default dummy implementations */
extension Messaging {
    func sendMessage(message: String) { }
    func sendMessage(message: String, count: Int = 1) { }
}

class Messager: Messaging {
    func sendMessage(message: String, count: Int = 1) {
        print(message + "\(count)")
    }
}

var a = Messager()
a.sendMessage("Hello world #")
// prints "Hello World #1"

但请注意,您的类的实例会将sendMessage个函数列为可用的类方法;其中一个是你的函数,另一个是虚拟默认实现。

编辑之前关于不同类型参数的旧答案(我将其保留在此处,因为它可能是所有参数属于同一类型的可能替代方案)

最后,您可以使用variadic parameters

protocol Messaging {
    func sendMessage(messages: String...)
}

class Messager: Messaging {
    func sendMessage(messages: String...) {
        for message in messages {
            print(message)
        }
    }
}

var a = Messager()

a.sendMessage("Hello", "World", "!")
  

可变参数接受零个或多个指定类型的值。   您使用可变参数来指定参数可以   调用函数时传递了不同数量的输入值。   通过插入三个句点字符来写出可变参数(...)   在参数的类型名称之后。