3

(if someone can suggest a better title, please do)

The following code does not compile with an error Type 'ObserverClass' does not conform to protocol 'Observer', and compiler suggests a fix by declaring var object: ObservedObject.

class ObservedObject {}
class ObservedObjectSubclass: ObservedObject {}

protocol Observer {
    var object: ObservedObject { get }
}

class ObserverClass: Observer { // Type 'ObserverClass' does not conform to protocol 'Observer'

    // suggested:
    // var object: ObservedObject

    var object: ObservedObjectSubclass = ObservedObjectSubclass()
}

The way i see it - ObservedObjectSubclass is ObservedObject, and so object property is guaranteed to be of type ObservedObject as required by the protocol.

(The same is true, if using protocol conformance instead of subclassing - below)

protocol ObservedObjectProtocol {}
protocol Observer {
    var object: ObservedObjectProtocol { get }
}

class ObservedObject: ObservedObjectProtocol {}

class ObserverClass: Observer { // same error
    var object: ObservedObject = ObservedObject()
}

Why is compiler unhappy? Is it current limitation, or the compiler is actually right and there is some logical constraint?

user1244109
  • 2,024
  • 2
  • 23
  • 24

1 Answers1

5

When you define a variable in a protocol and assign a type to it, that is going to be a concrete type, so you cannot change it to a subclass of that type when conforming to the protocol. The type of the variable declared in the conforming class must be the same type as declared in the protocol, it cannot be a covariant (inheritance related) type.

You can fix the second error by creating an associatedType for your Observer protocol, which inherits from ObserverObject then you can define object to be of the same type as your associated type. Then you can make your ObserverClass have a property object of type ObservedObjectSubclass.

class ObservedObject {}
class ObservedObjectSubclass: ObservedObject {}

protocol Observer {
    associatedtype ObjectSubclass: ObservedObject
    var object:ObjectSubclass { get }
}

class ObserverClass: Observer {
    var object = ObservedObjectSubclass()
}
Dávid Pásztor
  • 40,247
  • 8
  • 59
  • 80
  • "defined object as a computed property inside the Observer protocol" protocols do not make requirements regarding computed/stored; it could be either – user1244109 Jan 25 '18 at 00:58
  • Per your second point: does 'Concrete type' mean you cannot substitute with subclass? Again, in my view, it's one of the main points of inheritance. Also, my question was if there is any logical constraint - i.e. why would compiler not allow subclasses. – user1244109 Jan 25 '18 at 01:05
  • @user1244109 right, I was thinking about protocol extensions, so my point about computed properties wasn't correct, you are right. As for my second type, yes, as I've shown in my answer, you need to use an associated type if you want to be able to use inherited classes as well. This is the case, because when defining a protocol, each class conforming to the protocol must copy the variable and function declarations in the exact same way (unless you make them generic by introducing an associatedtype requirement). – Dávid Pásztor Jan 25 '18 at 01:14
  • Just think about trying to do the same with classes. If you inherit a function from another class, you can't change the function signature to make one of the input arguments a subclass of the input argument in the parent class's implementation of the same function. Inheritance allows you to use subclasses in place of their parent classes (so for instance class `ObserverClass: Observer { var object: ObservedObject = ObservedObjectSubclass() }` would conform to your original `Observer` protocol, since the type of the variable would be the same as in the protocol sub. – Dávid Pásztor Jan 25 '18 at 01:21
  • Imagine the case of `let observer: Observer = ObserverClass()` followed by `observer.object = DifferentObservedObjectSubclass()`. This is why it must exactly conform to the protocol. – Samah Jan 25 '18 at 01:23
  • okay, point taken - it's the signature, that must be exact. I can have the subclass object in, but the signature must copy. Same with function parameters - you can call it with subtypes as parameters, but the signature must match. – user1244109 Jan 25 '18 at 01:26
  • @Samah `DifferentObservedObjectSubclass` is still a `ObservedObject`. Additionally, the protocol only requires `{ get }` – user1244109 Jan 25 '18 at 01:29
  • @user1244109 Sure, but in my example `observer` is declared with type `Observer`, so the compiler can't know which type it is. – Samah Jan 25 '18 at 01:30
  • @DávidPásztor i will accept the answer, but i'l appreciate if you edit it to reflect that *signature must match* – user1244109 Jan 25 '18 at 01:31
  • @user1244109 since in my answer I'm talking about variables rather than functions, talking about signatures wouldn't be appropriate, since we only talk about _function signature_s, which are actually just the type of the function. I just mentioned functions in comments to make the example more clear. However, I've added another sentence to the first paragraph of my answer to make it more clear. – Dávid Pásztor Jan 25 '18 at 01:36