Reputation: 2932
I have a protocol with a method. I have thought that methods can be replaced with closures by the same name, but it doesn't seem to work:
protocol Foo {
func bar() // Type: Void -> Void
}
class X: Foo {
func bar() { }
}
class Y: Foo { // Compiler: doesn't conform to protocol Foo
let bar: Void->Void = {}
}
Is there a way to make this work? I want to override the methods behavior for a Test Stub implementation. Currently, I'd have to do this, which I'd like to shorten:
class Z: Foo {
var barClosure: Void -> Void = {}
func bar() {
barClosure()
}
}
let baz = Z()
baz.barClosure = { /* ... */ }
baz.bar() // Calls the closure replacement
Upvotes: 2
Views: 3615
Reputation: 40963
The func
keyword does a little bit more magic behind the scenes that you can’t replicate with properties – especially in the case of classes, where functions can be overridden, so vtables need to be built etc.
That said, if you were going to replace methods using closure expressions, you’d need to do more than the code you gave. The equivalent of this:
struct A {
let x: Int
func f() {
println("In f(), x is \(a.x)")
}
}
would be more like this:
struct A {
let x: Int
// returns a function that takes A objects, and
// returns a function that captures them
static let f: (A)->()->() = { a in
{ ()->() in println("In f(), x is \(a.x)") }
}
// equivalent of the dot notation call of f
var f: ()->() {
return A.f(self)
}
}
This replicates how struct methods actually work, and allows you to do all the same things an f
method does:
let a = A(x: 5)
// static version of f
let A_f = A.f
// that static version bound to a self:
let f = A_f(a)
f()
// the above is equivalent to:
a.f()
But this still isn’t enough for A
to conform to a protocol that requires an f()
method.
Upvotes: 2
Reputation: 12035
You declared the protocol to have a function, bar()
, but in class Y
, you just have a constant instead of a function, this is the problem. But if you want to have something like in class Y
, you should change the protocol to:
protocol Foo {
var bar: () -> () {get set}
}
And implement like that:
class Test: Foo {
private var _bar: (() -> ())?
var bar: () -> () {
get {
return {}
}
set {
self._bar = newValue
}
}
}
UPDATED
If you to shorten your class, you can use something like that:
protocol Foo {
var barClosure: Void -> Void {get set}
}
class Z: Foo {
var barClosure: Void -> Void = {
//do here something
}
}
let a = Z()
a.barClosure()
Upvotes: 2
Reputation: 2932
Thanks to @Dániel Nagy, I was able to figure out what options I have. The protocol should require a closure. This way, the client code won't change, as closure calls are identical to method calls.
let
) in production codevar
) in test code to provide alternate implementations in test cases, like mock observers doHere's a modified example which works well in a Playground by returning strings:
protocol Foo {
var bar: () -> String { get }
}
class X: Foo {
// cannot be overwritten
let bar: () -> String = { return "default x" }
}
class Y: Foo {
private let _bar: () -> String = { return "default y" }
// Can be overwritten but doesn't have any effect
var bar: () -> String {
get {
return _bar
}
set {
}
}
}
class Z: Foo {
// Can be overwidden
var bar: () -> String = {
return "default z"
}
}
let bax = X()
bax.bar() // => "default x"
// bax.bar = { /* ... */ } // Forbidden
let bay = Y()
bay.bar() // => "default y"
bay.bar = { return "YY" }
bay.bar() // => "default y"
let baz = Z()
baz.bar() // => "default z"
baz.bar = { return "ZZ" }
baz.bar() // => "ZZ"
Upvotes: 2