Swift – Inheritance

Dědičnost je jedna z věcí, která odlišuje třídy od struktur a dalších typů. Dědí se metody, properties a další charakteristiky. A Swift taky konečně přináší klíčové slovo @final , které zakazuje dědění.

Nebudu tady rozepisovat co je třída, co to znamená dědičnost, apod. To každý ví a kdo ne, ať si to nastuduje. Cílem je ukázat co je nového ve Swiftu.

Protože Swift je beta, zatím není umožněna práce s viditelností properties, metod, … pomocí public , private , protected , apod. Ale víme, že se na tom pracuje a bude to co nevidět. Všechno je prozatím public . Just FYI.

V Objective-C je základní třídou NSObject . Swift nic takového nemá. Resp. má, ale to je implementační detail. Pokud to někoho zajímá, tak si může přečíst Inside Swift. Thx to Tomáš Linhart.

class SomeClass {
}

Podtřída (subclass) se definuje následovně.

class ExtendedSomeClass: SomeClass {
}

Na rodiče (superclass) se odkazuje, stejně jako v Objective-C, pomocí klíčového slova super .

Metody

Máme základní třídu Shape . Property name obsahující název tvaru. Dále metodu area() vracející obsah tvaru. A metodu description() , která vrací textový popis.

class Shape {
  let name: String

  init( name: String) {
    self.name = name
  }

  func area() -> Double {
    return 0
  }

  func description() -> String {
    return "My name is (self.name) and I'm covering (self.area()) pixels of your screen."
  }
}

To je naše základní třída z které si vytvoříme obdélník.

class Rectangle: Shape {
  var width: Double = 0
  var height: Double = 0

  init() {
    super.init( name: "Rectangle" )
  }

  override func area() -> Double {
    return self.width * self.height
  }
}

V inicializátoru zavoláme inicializátor rodičovské třídy (superclass) spolu se jménem tvaru. Všimněte si, že se v tomto případě nepoužívá override . Inicializátory budou probrány v dalším postu.

A potom tu máme metodu area() počítající plochu obdélníku. Protože překrýváme (dovolil jsem si použít překlad od p. Pecinovského) metodu rodiče, musíme ji označit klíčovým slovem override .

var rect = Rectangle()
rect.width = 10
rect.height = 10
rect.area()
// 100.0
rect.description()
// My name is Rectangle and I'm covering 100.0 pixels of your screen.

Z Rectangle  můžeme udělat Shape  a zrovna uvidíme polymorfismus v akci.

let shape = rect as Shape
shape.area()
// 100.0
shape.description()
// My name is Rectangle and I'm covering 100.0 pixels of your screen.

Překrývat lze instanční metody i metody typu (třídy).

Properties

Properties můžeme překrývat stejně jako metody (instanční a typu). A to včetně observerů. Opět musíme použít klíčové slovo override .

class Progress {
  var progress: Double = 0.0
}

class LimitedProgress : Progress {
  override var progress: Double {
    get {
      return super.progress
    }
    set {
      super.progress = min( max( newValue, 0 ), 1.0 )
    }
  }
}

var p = Progress()
p.progress = -1.0
p.progress          // -1.0
p.progress = 2.0
p.progress          // 2.0
p.progress = 0.5
p.progress          // 0.5

var lp = LimitedProgress()
lp.progress = -1.0
lp.progress         // 0.0
lp.progress = 2.0
lp.progress         // 1.0
lp.progress = 0.5
lp.progress         // 0.5

U překrývání properties platí pár pravidel:

  • pokud překryjeme setter, musíme překrýt i getter,
  • z read only property můžeme udělat read write a musíme implementovat setter i getter,
  • z read write property nemůžeme udělat read only.

U překrývání observerů platí ještě navíc tato pravidla:

  • nelze překrýt observery u konstant a read only computed properties (nejdou změnit),
  • pokud překryjeme setter, nelze překrýt observer a sledovat novou hodnotu musíme v našem novém setteru.class Progress {
    var progress: Double = 0.0 {
    willSet {
    NSLog( “Progress.progress will be changed: (progress) -> (newValue)” )
    }
    }
    }

    class LimitedProgress : Progress {
    override var progress: Double {
    willSet {
    NSLog( “LimitedProgress.progress will be changed: (progress) -> (newValue)” )
    }
    }
    }

    var lp = LimitedProgress()
    lp.progress = 100

    // LimitedProgress.progress will be changed: 0.0 -> 100.0
    // Progress.progress will be changed: 0.0 -> 100.0

V překrytém observeru není nutné volat super , děje se tak automaticky. Nejdříve se volají observery potomka a potom rodiče.

@final

Objective-C nemá ekvivalent @final . Zakázat dědění lze pro celou třídu (@final class … ), property (@final var … ), metodu (@final func … ), metodu typu (@final class func … ) a subscriptu (@final subscript … ). Blbý na tom je, že třeba @final var …  mi znemožňuje přidání observerů. Níže uvedené kód je nepřeložitelný.

class Progress {
  @final var progress: Double = 0.0
}

class LimitedProgress : Progress {
  override var progress: Double {
    willSet {
      NSLog( "LimitedProgress.progress will be changed: (progress) -> (newValue)" )
    }
  }
}

No a protože zatím nemáme KVO, apod. tak vlastně ani nevím kdy se hodnota progress  změní. Nemám to jak zjistit. Reportováno, tak snad se toho brzo dočkám.

Snad jsem na nic nezapomněl. Příště se vrhnem na inicializaci tříd.