Swift for the Java Guy Part 2: The Basics
This article goes over the basics of class construction in Swift, and compares and contrasts class construction in Swift and Java.
Join the DZone community and get the full member experience.
Join For FreeIn Part 1 of this series we looked at how to get Swift up and running. In this part, we will look the Differences between Java and Swift at a basic language level by creating the Swift classes and comparing them to Java. For this article, we will go over the basics of class construction.
Firstly, What's the Same?
Both languages are fundamentally statically typed class-based OO languages with single inheritance and interfaces. Furthermore, Swift includes the normal set of features that Java has including:
- Try-Catch style Exception handling
- C-Style syntax
- Generics
- First class and high order functions
What's Different?
Swift is very different in a key set of areas including
- Reference based memory management using Automated Reference counting as opposed to a garbage collector.
- Swift classes don't inherit from a base object class.
- Swift classes do not need to be declared in a file that follows the Namespace/Package and Class name pattern.
- All methods in Swift are functions, Void in Swift is essentially a type alias for an empty Tuple.
- Swift only has three access modifiers, private and public which behave similarly to the Java equivalents. It also has internal, which is Swift's equivalent of default access; Swift, however, has no protected modifier.
- Swift interface (or protocols as they are called in Swift parlance) cannot have default implementations (although you can achieve the same effect with extensions).
- Enums in Swift are "different". The best way I can describe is that Swift Enums are basically a set named Tuples which are grouped together in an Enum declaration, each one can declare it's own structure.
What Swift Has That Java Does Not
Swift has a range of features that Java does not have:
- Value Types (Structs)
- Tuples
- Extension types
- Operator overloading
- Type inference
- Language level optional monads and strict null checking
- Properties
- Destructors
- Values and variables as opposed to just variables ala Scala
- Nested functions (AKA functions inside functions)
- Pattern matching
Declaring Your First Class in Swift
For this example, I create simple class called "Animal" with a single void method makeSound:
class Animal {
func makeSound() -> Void {
print("Roar!!")
}
}
Animal().makeSound()
A few observations:
- Swift methods are explicitly declared with the func keyword and have the return type after the "->" operator which is the opposite order of Java approach in terms of declaration.
- The last line creates a new instance of Animal and calls the makeSound method. Note that Animal also has an implicit no-args constructor, also there is no new keyword.
Adding a Property
So obviously not all animals roar, to fix this we add a property sound to the class by adding the following:
class Animal {
var sound:String = "Croak"
func makeSound() -> Void {
print(sound)
}
}
let animal = Animal()
animal.sound = "Roar"
animal.makeSound()
By default, all variables (denoted by the var keyword) declared as members are properties. What isn't visible here is that when you set the sound property it's actually accessed via accessor methods that are created implicitly. Another point worth mentioning is that the animal variable is not a variable at all but rather a constant as indicated by the let keyword, the let keyword is Swift's equivalent of Scala's val keyword.
Constructors
In the previous example, I set roar the default value of "Croak". It would be better if we could pass this information via a constructor and make the sound property immutable, to do this we change the class as follows:
class Animal {
let sound:String
init(sound:String) {
self.sound = sound
}
func makeSound() -> Void {
print(sound)
}
}
let animal = Animal(sound:"Roar")
animal.makeSound()
Constructors in Swift are slightly different to Java in terms of syntax in that they are defined inside a block defined with the keyword init. These blocks can take parameters as you would have in a Java constructor. I also changed the sound property from var to let, this means that the sound property cannot be reassigned once it's been assigned. It's also worth showing here that Swift requires you to use named parameters when you invoke the constructor, this will be familiar to anyone who has ever used Objective-C.
Adding an Interface (or Protocol)
Since Animals aren't the only things that make sound, we can pull this functionality up into a Protocol called Audible which can be implemented by the Animal class.
protocol Audible {
func makeSound() -> Void
}
class Animal:Audible {
let sound:String
init(sound:String) {
self.sound = sound
}
func makeSound() -> Void {
print(sound)
}
}
let audible:Audible = Animal(sound:"Roar")
audible.makeSound()
Here I added a protocol with a makeSound method, aside from the protocol keyword, this should look familiar to most Java developers. The only change to the Animal class is that it implements Audible. The syntax for extension and implementation is the colon syntax which works the same way as C#. I also explicitly typed my audible value to be of type Audible just to force an upcast.
Properties on Protocols
With property properties onto Protocols. For this example, I created another Protocol called Named which just has a String property; name
protocol Named {
var name:String {get set}
}
protocol Audible {
func makeSound() -> Void
}
class Animal: Audible, Named {
let sound:String
var name:String
init(sound:String, name:String) {
self.sound = sound
self.name = name
}
func makeSound() -> Void {
print(sound)
}
}
let animal = Animal(sound:"Roar", name:"Lion")
print(animal.name)
animal.makeSound()
This example also shows mutability of the name property using the get and set keywords. Interestingly I still have to define the actual name variable of the Animal class. Keeping in mind that a Protocol defines behavior, a property on a Protocol only indicates how the property is structured not how it's stored, that is the responsibility of the implementing class.
Inheritance
For our final addition lets create a class called LivingOrganism which implements the Named protocol from the previous example and make Animal extend that. I also add a default constructor which takes the name property as a parameter
class LivingOrganism:Named {
var name:String
init(name:String) {
self.name = name
}
}
class Animal: LivingOrganism, Audible {
let sound:String
init(sound:String, name:String) {
self.sound = sound
super.init(name:name)
}
func makeSound() -> Void {
print(sound)
}
}
Again note the C# style inheritance syntax. Another interesting thing to note is the use of the super keyword which behaves much like super in Java. In this case, I invoked the super class init method inside Animal. One difference to Java is that Swift requires the local variables to be initialized first before invoking the super constructor, hence the call being the last line of Animal's constructor. This is the reverse of Java.
So there we have it, a basic overview of class construction in Swift. In the next part, we will explore Some of the additional features of Swift such as Tuples and Strict Null checking with optional monads.
Published at DZone with permission of , DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments