Declaring a class that works with two constrained generic types

Now, it is time to code another protocol that will be used as a constraint later, when we define another class that takes advantage of generics with two constrained generic types. The following lines show the code for the DeeJayProtocol protocol. The public modifier followed by the protocol keyword and the protocol name, DeeJayProtocol, composes the protocol declaration, as follows. The code file for the sample is included in the swift_3_oop_chapter_06_10 folder:

    public protocol DeeJayProtocol { 
      var name: String { get } 
     
      init(name: String) 
     
      func playMusicToDance() 
      func playMusicToSing() 
    } 

The protocol declares a name: String read-only stored property and two method requirements: playMusicToDance and playMusicToSing. As you

learned in the previous chapter, the protocol includes only the method declaration because the classes that conform to the DeejayProtocol protocol will be responsible for providing the implementation of the name stored property and the other two methods.

In addition, the protocol specifies an initializer requirement. The initializer requires a name argument; therefore, we will make sure that we will be able to create an instance of any class that conforms to this protocol by providing a value to a name argument during the initialization.

Now, we will declare a class named HorseDeeJay that conforms to the previously defined DeeJayProtocol protocol. We can read the class declaration as "The HorseDeeJay class implements the DeeJayProtocol protocol." Take a look at the following code. The code file for the sample is included in the swift_3_oop_chapter_06_10 folder:

    open class HorseDeeJay: DeeJayProtocol { 
      open let name: String 
     
      public required init(name: String) { 
        self.name = name 
      } 
     
      open func playMusicToDance() { 
        print("My name is (name). Let's Dance.") 
        // Multiple musical notes emoji icon 
        print(String(UnicodeScalar(0x01F3B6)!)) 
        // Dancer emoji icon 
        print(String(UnicodeScalar(0x01F483)!)) 
      } 
     
      open func playMusicToSing() { 
        print("Time to sing!") 
        // Guitar emoji icon 
        print(String(UnicodeScalar(0x01F3B8)!)) 
      } 
    } 

The HorseDeeJay class declares an initializer that assigns the value of the required name argument to the name read-only stored property. The class declares a name read-only stored property.

The playMusicToDance method prints a message that displays the horse DJ name and invites the party members to dance. Then, it prints the multiple musical notes and dancer emoji icons. The playMusicToSing method prints a message that invites the party members to sing. Then, it prints a guitar emoji icon.

The following lines declare a subclass of the previously created Party<AnimalElement> class that takes advantage of generics to work with two constrained types. The type constraints declaration is included within angle brackets (< >). In this case, we have two generic type parameters: AnimalElement and DeeJayElement. The generic type parameter named AnimalElement must conform to the AnimalProtocol protocol and also the Equatable protocol, as it happened in the Party<AnimalElement> superclass. The generic type parameter named DeeJayElement must conform to the DeeJayProtocol protocol. The where keyword allows us to add a second constraint to the generic type parameter named AnimalElement. This way, the class specifies constraints for both the AnimalElement and DeeJayElement generic type parameters. Don't forget that we are talking about a subclass of Party<AnimalElement>; therefore, we inherited a required initializer that only receives a leader argument. We overrode this required initializer with code that calls the fatalErrorfunction to print a message and stop execution. This way, we make sure that the inherited required initializer cannot be used with this class. The following code highlights the lines that use the DeeJayElement generic type parameter. The code file for the sample is included in the swift_3_oop_chapter_06_10 folder:

    open class PartyWithDeeJay<AnimalElement: 
    AnimalProtocol, DeeJayElement: DeeJayProtocol>: 
    Party<AnimalElement> where AnimalElement: Equatable { 
      public var deeJay: DeeJayElement 
     
      init(leader: AnimalElement, deeJay: DeeJayElement) { 
        self.deeJay = deeJay 
        super.init(leader: leader) 
      } 
 
      public required init(leader: AnimalElement) { 
        fatalError("init(leader:) has not been implemented") 
      } 
     
      open override func dance() { 
        deeJay.playMusicToDance() 
        super.dance() 
      } 
     
      open override func sing() { 
        deeJay.playMusicToSing() 
        super.sing() 
      } 
    } 

Now, we will analyze many code snippets to understand how the code included in the PartyWithDeeJay<AnimalElement, DeeJayElement> class works. The following line starts the class body and declares a public deeJay stored property of the type specified by DeeJayElement:

    public var deeJay: DeeJayElement 

The following lines declare an initializer that receives two arguments--leader and deeJay--whose types are AnimalElement and DeeJayElement. The arguments specify the first party leader, the first member of the party, and the DJ that will make the party members dance and sing. Note that the initializer calls the initializer defined in its superclass--that is, the Party<AnimalElement> init method--with leader as an argument:

    init(leader: T, deeJay: K) { 
      self.deeJay = deeJay 
      super.init(leader: leader) 
    } 

The following lines declare a dance method, which overrides the method with the same declaration included in the superclass. The code calls the deeJay.playMusicToDance method and then the super.dance method, that is, the dance method defined in the Party<AnimalElement> superclass:

public override func dance() { 
    deeJay.playMusicToDance() 
    super.dance() 
} 

Finally, the following lines declare a sing method, which overrides the method with the same declaration included in the superclass. The code calls the deeJay.PlayMusicToSing method and then calls the super.sing method, that is, the sing method defined in the Party<AnimalElement> superclass:

    public override func sing() { 
      deeJay.playMusicToSing() 
      super.sing() 
    } 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.117.4