Swift und Java

Java und Apple Swift

Apple Swift – Eine moderne Programmiersprache

Mit der Programmiersprache Swift hat Apple ein modernes Werkzeug für die Anwendungsentwicklung für Mac und iOS bereit gestellt. Diese Sprache löst das von vielen Entwicklern als etwas gewöhnungsbedürftig und sperrig betrachtete Objective C ab.  Mit der aktuellen Version 3 steht eine erste vollständige und unabhängige Implementierung zur Verfügung. Die anstehende Version 4 wird weitere Features einführen, jedoch keine Änderungen an der Core-Sprache mehr vornehmen.

Swift wurde von Apple als Open Source-Initiative freigegeben und steht damit auch für andere Plattformen zur Verfügung, z.B. für Ubuntu. Eine durchaus beachtenswerte Abkehr von der bisherigen Abschottung der Apple-Produkte. Allerdings gilt dies natürlich nur für den Kern der Sprache, die App-Programmierung muss aus Lizenz-Gründen weiterhin unter Apple Hardware und Betriebssystem erfolgen. Diese Einschränkung ist für diesen Artikel allerdings nicht relevant, da ich mich hier bewusst auf den Vergleich der Programmiersprachen konzentrieren möchte; für einen (durchaus interessanten!) Vergleich von Google Android  und Apple Swift gibt es andere Artikel.

Swift vereinigt etablierte Konzepte aus Sprachen wie Java, C#, JavaScript und natürlich auch Objective C und ergänzt diese durch eigene Ideen. Damit finden Programmierer einesteils recht viel Bekanntes, aber eben auch unerwartete und damit überraschende Konstrukte.  Hier möchte ich auf die Unterschiede zu Java eingehen, die aus meiner Erfahrung heraus relevant sind.

Swift und Java – Welche Konzepte sind interessant?

Für Java-Entwickler ist Swift auch deshalb interessant, weil einige Konzepte durchaus auch in einer zukünftigen Java-Version Einzug halten könnten. Weiterhin gibt es doch eine Reihe von Besonderheiten, die den Einstieg in Swift erschweren könnten.

Konsequente Type Inference

Swift ist wie Java streng und statisch typisiert. Damit prüft der Swift-Compiler, dass einer einmal deklarierten Variable kein Wert eines anderen Datentyps zugewiesen werden kann.

Allerdings ist eine Typ-Angabe bei der Deklaration optional; der Typ kann meistens aus dem Typen der Zuweisung bestimmt werden:

 

var message = "Hello" //message is a String
message = "Hello!"    //OK
//message = 42        //Compiler Error

var message2:String   //No type inference

Optionals

Sehr gut gelöst ist meiner Meinung nach der Umgang mit Null-Werten, in Swift nilgenannt. Einer Variablen muss ein Nicht-Null-Wert zugewiesen werden, es sei denn, sie wird explizit als Optional-Typ deklariert:

var message = "Hello"          //message non optional String
//message = nil                //Compiler error

var message2:String? = "Hello" //message2 an optional String
message2 = nil                 //OK

Optionals sind in Swift eigene Typen, so dass der Compiler bei Zuweisungen die normale Typ-Prüfung durchführt und Fehler erkennt:

//message = message2  //Compiler error: String? not of type String

Optionale Typen müssen vor ihrer weiteren Verwendung “ausgepackt”=unwrapped werden. Dafür gibt es in Swift zwei Möglichkeiten:

  • Explizites Unwrap mit dem !-Operator
  • Sicheres Unwrap über die if -let -Konstruktion:
message = message2! 

if let x = message2 {
   print (x)
}

Obwohl diese Sequenzen für einen Java-Entwickler erst einmal merkwürdig aussehen liegt der Vorteil auf der Hand: Bereits der Compiler erkennt  Zuweisungen von Null-Werten, so dass der Entwickler auf null-Prüfungen oder das Fangen einer NullPointerException verzichten kann.

Und dann haben wir noch das “Optional Chaining” beim Zugriff auf Eigenschaften eines Objekts:

if (message2!.hasPrefix("H"){ //Unsafe
   //...
}

if (message2?.hasPrefix("H"){ //Safe
   //...
}

Falls message2 nil sein sollte wird die hasPrefix-Methode nicht ausgeführt.

Klassen und Strukturen in Swift

Swift kennt zwei unterschiedliche Definitionen eines benutzerdefinierten Datentyps: Klassen und Strukturen.

struct Address{
    var city: String, street: String
}
class Person{
    private var name:String
    init(name: String){
        self.name = name
    }
    var Name: String{
        get {
            return name
        }
    set {
            print ("setting lastname...")
            name = newValue
        }
    }
    func info() -> String {
        var greeting =  "Hello, my name is \(name)"
        return greeting
    }
}

Während Instanzen von Klassen wie in Java stets über Referenzen angesprochen werden, werden Strukturen immer als Werte angesehen:

let address1 = Address(city: "München", street: "Marienplatz")
let address2 = address1

let person1 = Person(name: "Hugo")
let person2 = person1
Swift - Der Unterschied zwischen Strukturen und Klassen
Der Unterschied zwischen Strukturen und Klassen

Ganz besonders verwirrend für Java-Entwickler ist, dass alle Collections-Implementierungen in Swift als Strukturen realisiert wurden!

Weitere Besonderheiten sind eher unter “syntactic sugar” zu sehen:

  • Einfache Definition von Properties durch get– und set-Blöcke
  • Bei Strukturen werden die Konstruktoren automatisch angelegt

Extensions

Alle Klassen und Strukturen sind in Swift “Open for Extension”. Das bedeutet, dass auch ohne Vererbung vorhandene Klassen jederzeit erweitert werden können. Nur zur Klarstellung: Dies gilt selbstverständlich auch für die Klassen der Standard-Bibliothek!

Zur Definition wird das Schlüsselwort extension benutzt. Im Folgenden eine (zugegebenermaßen triviale) Erweiterung der Person-Klasse sowie ein neuer Konstruktor für String:

extension Person{
    func sayHello{
        return "Hello",
    }
}

extension String{
    init?(_ address: Address){
        self.init("Die Adresse \(address.city)")
    }
}

 

Extensions können eine Klasse nicht mit neuen Attributen ausstatten. get– und set-Blöcke können jedoch verwendet werden.

Verschiedenes

  • Neben Klassen und Strukturen unterstützt Swift auch Enums und Tuples
  • Als Zeichen für Namen von Variablen oder Funktionen können beliebige Unicode-Zeichen benutzt werden.
  • Funktionsparameter haben eine  internen und gegebenenfalls einen öffentlichen Namen, der beim Aufruf mitgegeben werden muss:
    func func1(message: String){
       print(message)
    }
    
    func1(message: "Hello")
    //func1("Hello) //compiler error
    
    func func2(message m: String){
       print(m)
       //print (message)
    }
    
    func2(message: "Hello")
    //func2("Hello) //compiler error
    //func2(m: "Hello) //compiler error
    

    Sollen die Parameter nicht benannt sein wird der Unterstrich als öffentlicher Name benutzt:

    func func3(_ message: String){
       print(message)
    }
    
    func3("Hello")
    //func3(message: "Hello") //compiler error
  • Die Swift-Installation enthält eine REPL, so dass Sequenzen und einfache Anwendungen auch ohne Installation einer Entwicklungsumgebung ausgeführt werden können.
  • Schnittstellen heißen in Swift protocol, nicht interface.
  • Mit der String Interpolation werden Variablen bzw. Ausdrücke direkt in Zeichenketten ausgewertet:
let multiplier = 3.0
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"
  • Operatoren können jederzeit geändert oder für eigene Datentypen definiert werden
func + (left: Int, right: Int) -> Int{ //just for fun
    return left - right
}

func == (left: Person, right: Person) -> Bool{ //all people are equal
    return true
}
func + (left: Person, right: Person){ //adding to people = marriage, suggestions for '-'?
    left.marry(right)
}
  • Auch neue Operatoren sind einfach zu deklarieren. So wird im folgenden Beispiel das Unicode-Zeichen für das Herz-Symbol als Operator zwischen zwei Personen definiert:
infix operator ❤ {associativity left precedence 200}

func ❤ (left: Person, right: Person) -> Person{
    left.marry(right)
    return left
}
//In action:

rainer ❤ carola

 

Die Beispiele

Die im Artikel beschriebenen Code-Fragmente stehen über meinen GitHub-Account zur freien Verfügung und können gerne für eigene Experimente benutzt werden. Die Sourcen gibt es hier.

Die Beispiele benötigen eine Swift-Installation, ich habe hierzu die Ubuntu-Installation benutzt.


Seminar zum Thema

Dr. Rainer Sawitzki / Dr. Rainer Sawitzki

Nach seinem Studium der Physik und anschließender Promotion Wechsel in die IT-Branche. Seit mehr als 20 Jahren als Entwickler, Berater und Projektleiter vorwiegend im Bereich Java und JavaScript unterwegs. Parallel dazu in der Entwicklung und Durchführung von hochwertigen Seminaren für die Integrata im Einsatz.

Schreibe einen Kommentar