Now, we will declare a class that specifies that it conforms to the ComicCharacter
protocol in its declaration in the Playground. Instead of specifying a superclass, the class declaration includes the name of the previously declared ComicCharacter
protocol after the class name (AngryDog
) and the colon (:
). We can read the class declaration as "the AngryDog
class conforms to the ComicCharacter
protocol."
However, the class doesn't implement any of the required properties and methods specified in the protocol, so it doesn't really conform to the ComicCharacter
protocol, as shown in the following code. The code file for the sample is included in the swift_3_oop_chapter_05_02
folder:
open class AngryDog: ComicCharacter { }
The Playground execution will fail because the AngryDog
class doesn't conform to the ComicCharacter
protocol, so the Swift compiler generates the following errors and notes. We will see similar error messages in the Swift REPL and in the Swift Sandbox:
error: type 'AngryDog' does not conform to protocol 'ComicCharacter' public class AngryDog: ComicCharacter { ^ note: protocol requires property 'nickName' with type 'String'; do you want to add a stub? var nickName: String { get set } ^ note: protocol requires function 'drawSpeechBalloon(message:)' with type '(String) -> ()'; do you want to add a stub? func drawSpeechBalloon(message: String) ^ note: protocol requires function 'drawSpeechBalloon(destination:message:)' with type '(ComicCharacter, String) -> ()'; do you want to add a stub? func drawSpeechBalloon(destination: ComicCharacter, message: String) ^ note: protocol requires function 'drawThoughtBalloon(message:)' with type '(String) -> ()'; do you want to add a stub? func drawThoughtBalloon(message: String) ^
Now, we will replace the previous declaration of the empty AngryDog
class with a class that tries to conform to the ComicCharacter
protocol, but it still doesn't achieve its goal. The following lines show the new code for the AngryDog
class. The code file for the sample is included in the swift_3_oop_chapter_05_03
folder:
open class AngryDog: ComicCharacter { var nickName: String = String() func speak(message: String) { print("(nickName) -> "(message)"") } func think(message: String) { print("(nickName) -> ***(message)***") } func drawSpeechBalloon(message: String) { speak(message: message); } func drawSpeechBalloon(destination: ComicCharacter, message: String) { speak(message: "(destination.nickName), (message)") } func drawThoughtBalloon(message: String) { think(message: message) } init (nickName: String) { self.nickName = nickName } }
The Playground execution will fail because the AngryDog
class doesn't conform to the ComicCharacter
protocol; therefore, the Swift compiler generates the following errors and notes:
error: property 'nickName' must be declared public because it matches a requirement in public protocol 'ComicCharacter' var nickName: String = String() ^ public error: method 'drawSpeechBalloon(message:)' must be declared public because it matches a requirement in public protocol 'ComicCharacter' func drawSpeechBalloon(message: String) { ^ public error: method 'drawSpeechBalloon(destination:message:)' must be declared public because it matches a requirement in public protocol 'ComicCharacter' func drawSpeechBalloon(destination: ComicCharacter, message: String) { ^ public error: method 'drawThoughtBalloon(message:)' must be declared public because it matches a requirement in public protocol 'ComicCharacter' func drawThoughtBalloon(message: String) { ^ public
The public ComicCharacter
protocol specifies property and method requirements. Thus, when we declare a class that doesn't declare the required properties and methods at least as public
, the Swift compiler generates errors and indicates that they have to be declared at least as public
to match the protocol requirements. We can declare the required properties and methods with the open
access modifier because members declared as open
have the same accesibility level as members declared as public
and add the chance to be overridden. Think about open
as a superset of public
.
Whenever we declare a class that specifies that it conforms to a protocol, it must fulfill all the requirements specified in the protocol. If it doesn't, the Swift compiler will throw errors indicating which requirements aren't fulfilled, like what happened in the previous example. When we work with protocols, the Swift compiler makes sure that the requirements specified in protocols are honored in any class that conforms to them. Hence, when we modify a protocol, the Swift compiler needs to recompile the classes that conform to this protocol.
Finally, we will replace the previous declaration of the AngryDog
class with a class that really conforms to the ComicCharacter
protocol. The following lines show the new code for the AngryDog
class. The code file for the sample is included in the swift_3_oop_chapter_05_04
folder:
open class AngryDog: ComicCharacter { open var nickName: String = String() fileprivate func speak(message: String) { print("(nickName) -> "(message)"") } fileprivate func think(message: String) { print("(nickName) -> ***(message)***") } open func drawSpeechBalloon(message: String) { speak(message: message); } open func drawSpeechBalloon(destination: ComicCharacter, message: String) { speak(message: "(destination.nickName), (message)") } open func drawThoughtBalloon(message: String) { think(message: message) } init (nickName: String) { self.nickName = nickName } }
The AngryDog
class declares an initializer that assigns the value of the required nickName
argument to the nickName
stored property. In this case, the ComicCharacter
protocol doesn't include any initializer requirement, so the AngryDog
class can specify any desired initializer without restrictions.
The class declares the code for the two versions of the drawSpeechBalloon
method. Both methods call the private speak
method that prints a message with a specific format that includes the nickName
value as a prefix. In addition, the class declares the code for the drawThoughtBalloon
method, which invokes the private think
method, which also prints a message including the nickName
value as a prefix.
The AngryDog
class implements the property and methods declared in the ComicCharacter
protocol. However, the class also declares two fileprivate
members, specifically, two fileprivate
methods. We will be able to access these two methods only in its own defining source file.
Now, we will declare another class that implements the same protocol that the AngryDog
class implemented, that is, the ComicCharacter
protocol. The following lines show the code for the AngryCat
class. The code file for the sample is included in the swift_3_oop_chapter_05_04
folder:
open class AngryCat: ComicCharacter { open var nickName: String = String() open var age: UInt = 0 open func drawSpeechBalloon(message: String) { if (age > 5) { print("(nickName) -> "Meow (message)"") } else { print("(nickName) -> "Meeeooow Meeeooow (message)"") } } open func drawSpeechBalloon(destination: ComicCharacter, message: String) { print("(destination.nickName) === (nickName) ---> "(message)"") } open func drawThoughtBalloon(message: String) { print("(nickName) thinks: (message)") } init (nickName: String, age: UInt) { self.nickName = nickName self.age = age } }
The AngryCat
class declares an initializer that assigns the value of the required nickName
and age
arguments to the nickName
and age
stored properties. The class declares the code for the two versions of the drawSpeechBalloon
method. The version that requires only a message
argument uses the value of the age
property to generate a different message when the age
value is greater than 5
. In addition, the class declares the code for the drawThoughtBalloon
method.
The AngryCat
class implements the property and method requirements declared in the ComicCharacter
protocol. However, the class also declares an additional property that isn't required by the protocol: age
.
If we remove the open
keyword in the line that declares the nickName
stored property within the AngryCat
class, the class won't implement all the required members of the ComicCharacter
protocol, at least as public members; therefore, it won't conform to the protocol anymore. The code file for the sample is included in the swift_3_oop_chapter_05_05
folder:
var nickName: String = String()
The Playground execution will fail because the AngryCat
class doesn't conform to the ComicCharacter
protocol anymore, so the Swift compiler generates the following error:
error: property 'nickName' must be declared public because it matches a requirement in public protocol 'ComicCharacter' var nickName: String = String() ^ public
Thus, the compiler forces us to implement all the members of a protocol in all the classes that we indicate as conforming to a protocol. If we add the open
keyword again to the line that declares the nickName
property, we will be able to execute the code in the Playground without compiler errors. The code file for the sample is included in the swift_3_oop_chapter_05_06
folder:
open var nickName: String = String()
In this case, the ComicCharacter
protocol didn't specify any initializer requirements, so each class that conforms to the protocol can define its initializer without any constraint. AngryDog
and AngryCat
declare initializers with a different number of arguments.
3.138.181.196