Is There a Difference Between Swift 2.0 Protocol Extensions and Java/C# Abstract Classes

Is there a difference between Swift 2.0 protocol extensions and Java/C# abstract classes?

There are several important differences...

Protocol extensions can work with value types as well as classes.

Value types are structs and enums. For example, you could extend IntegerArithmeticType to add an isPrime property to all integer types (UInt8, Int32, etc). Or you can combine protocol extensions with struct extensions to add the same functionality to multiple existing types — say, adding vector arithmetic support to both CGPoint and CGVector.

Java and C# don't really have user-creatable/extensible "plain old data" types at a language level, so there's not really an analogue here. Swift uses value types a lot — unlike ObjC, C#, and Java, in Swift even collections are copy-on-write value types. This helps to solve a lot of problems about mutability and thread-safety, so making your own value types instead of always using classes can help you write better code. (See Building Better Apps with Value Types in Swift from WWDC15.)

Protocol extensions can be constrained.

For example, you can have an extension that adds methods to CollectionType only when the collection's underlying element type meets some criteria. Here's one that finds the maximum element of a collection — on a collection of numbers or strings, this property shows up, but on a collection of, say, UIViews (which aren't Comparable), this property doesn't exist.

extension CollectionType where Self.Generator.Element: Comparable {
var max: Self.Generator.Element {
var best = self[self.startIndex]
for elt in self {
if elt > best {
best = elt
}
}
return best
}
}

(Hat tip: this example showed up on the excellent NSBlog just today.)

There's some more good examples of constrained protocol extensions in these WWDC15 talks (and probably more, too, but I'm not caught up on videos yet):

  • Protocol-Oriented Programming in Swift
  • Swift in Practice

Abstract classes—in whatever language, including ObjC or Swift where they're a coding convention rather than a language feature—work along class inheritance lines, so all subclasses inherit the abstract class functionality whether it makes sense or not.

Protocols can choose static or dynamic dispatch.

This one's more of a head-scratcher, but can be really powerful if used well. Here's a basic example (again from NSBlog):

protocol P {
func a()
}
extension P {
func a() { print("default implementation of A") }
func b() { print("default implementation of B") }
}
struct S: P {
func a() { print("specialized implementation of A") }
func b() { print("specialized implementation of B") }
}

let p: P = S()
p.a() // -> "specialized implementation of A"
p.b() // -> "default implementation of B"

As Apple notes in Protocol-Oriented Programming in Swift, you can use this to choose which functions should be override points that can be customized by clients that adopt a protocol, and which functions should always be standard functionality provided by the protocol.

A type can gain extension functionality from multiple protocols.

As you've noted already, protocol conformance is a form of multiple inheritance. If your type conforms to multiple protocols, and those protocols have extensions, your type gains the features of all extensions whose constraints it meets.

You might be aware of other languages that offer multiple inheritance for classes, where that opens an ugly can of worms because you don't know what can happen if you inherit from multiple classes that have the same members or functions. Swift 2 is a bit better in this regard:

  • Conflicts between protocol extensions are always resolved in favor of the most constrained extension. So, for example, a method on collections of views always wins over the same-named method on arbitrary collections (which in turn wins over the same-named methods on arbitrary sequences, because CollectionType is a subtype of SequenceType).

  • Calling an API that's otherwise conflicting is a compile error, not a runtime ambiguity.

Protocols (and extensions) can't create storage.

A protocol definition can require that types adopting the protocol must implement a property:

protocol Named {
var name: String { get } // or { get set } for readwrite
}

A type adopting the protocol can choose whether to implement that as a stored property or a computed property, but either way, the adopting type must declare its implementation the property.

An extension can implement a computed property, but an extension cannot add a stored property. This is true whether it's a protocol extension or an extension of a specific type (class, struct, or enum).

By contrast, a class can add stored properties to be used by a subclass. And while there are no language features in Swift to enforce that a superclass be abstract (that is, you can't make the compiler forbid instance creation), you can always create "abstract" superclasses informally if you want to make use of this ability.

Abstract classes in Swift Language

There are no abstract classes in Swift (just like Objective-C). Your best bet is going to be to use a Protocol, which is like a Java Interface.

With Swift 2.0, you can then add method implementations and calculated property implementations using protocol extensions. Your only restrictions are that you can't provide member variables or constants and there is no dynamic dispatch.

An example of this technique would be:

protocol Employee {
var annualSalary: Int {get}
}

extension Employee {
var biweeklySalary: Int {
return self.annualSalary / 26
}

func logSalary() {
print("$\(self.annualSalary) per year or $\(self.biweeklySalary) biweekly")
}
}

struct SoftwareEngineer: Employee {
var annualSalary: Int

func logSalary() {
print("overridden")
}
}

let sarah = SoftwareEngineer(annualSalary: 100000)
sarah.logSalary() // prints: overridden
(sarah as Employee).logSalary() // prints: $100000 per year or $3846 biweekly

Notice that this is providing "abstract class" like features even for structs, but classes can also implement the same protocol.

Also notice that every class or struct that implements the Employee protocol will have to declare the annualSalary property again.

Most importantly, notice that there is no dynamic dispatch. When logSalary is called on the instance that is stored as a SoftwareEngineer it calls the overridden version of the method. When logSalary is called on the instance after it has been cast to an Employee, it calls the original implementation (it doesn't not dynamically dispatch to the overridden version even though the instance is actually a Software Engineer.

For more information, check great WWDC video about that feature: Building Better Apps with Value Types in Swift

What is Protocol Oriented Programming in Swift? What added value does it bring?

Preface: POP and OOP are not mutually exclusive. They're design paradigms that are greatly related.

The primary aspect of POP over OOP is that is prefers composition over inheritance. There are several benefits to this.

In large inheritance hierarchies, the ancestor classes tend to contain most of the (generalized) functionality, with the leaf subclasses making only minimal contributions. The issue here is that the ancestor classes end up doing a lot of things. For example, a Car drives, stores cargo, seats passengers, plays music, etc. These are many functionalities that are each quite distinct, but they all get indivisibly lumped into the Car class. Descendants of Car, such as Ferrari, Toyota, BMW, etc. all make minimal modifications to this base class.

The consequence of this is that there is reduced code reuse. My BoomBox also plays music, but it's not a car. Inheriting the music-playing functionality from Car isn't possible.

What Swift encourages instead is that these large monolithic classes be broken down into a composition of smaller components. These components can then be more easily reused. Both Car and BoomBox can use MusicPlayer.

Swift offers multiple features to achieve this, but the most important by far are protocol extensions. They allow implementation of a protocol to exist separate of its implementing class, so that many classes may simply implement this protocol and instantly gain its functionality.

Compare Protocol in Swift vs Interface in Java

Essentially protocols are very similar to Java interfaces except for:

  • Swift protocols can also specify properties that must be implemented (i.e. fields)
  • Swift protocols need to deal with value/reference through the use of the mutating keyword (because protocols can be implemented by structures, enumerations or classes).
  • you can combine protocols at any point using "Protocol Composition". This replaces the older swift protocol<A, B> way of protocol composition. For example, declaring a function parameter that must adhere to protocol Named and Aged as:
    func wishHappyBirthday(to celebrator: Named & Aged) {}

These are the immediately apparent differences for a Java developer (or at least what I've spotted so far). There's more info here.

reflect property value on swift 2.0

This is the usage of reflection is swift 2.

let mirror = Mirror(reflecting: obj)
for child in mirror.children {
guard let key = child.label else {
continue
}
let value = child.value

// use value or key here
}


Related Topics



Leave a reply



Submit