オブジェクトとクラス【Swiftイントロダクション#4】
クラスを定義する
クラスを定義するには、クラスの名前をclassのあとに記述します。プロパティの宣言は、定数や変数の宣言と同じように行います。関数やメソッドの宣言も同じように行います。
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
クラスはのインスタンスはクラス名の後に括弧()を付けて生成します。インスタンスのプロパティやメソッドにアクセスするにはドットシンタックスを使います。
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
上記のshapeクラスには、インスタンスが生成された時にインスタンスの初期化をするイニシャライザがありません。initを使ってイニシャライザを足しましょう。
class Shape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A \(name) with \(numberOfSides) sides."
}
}
上記のコードで、selfがこのクラスのnameプロパティとイニシャライザの引数のnameを区別するのに使われていることに注目してください。
クラスのインスタンスを生成する時に渡された引数がinitの中でnameに入れられています。
クラスが生成される時は、全てのプロパティに対して値が入っている必要があります。値は、宣言する時か初期化のタイミングで入れます。
//宣言時に値が入れられる
var numberOfSides: Int = 0
//初期化時に値が入れられる
var shape = Shape()
shape.numberOfSides = 7
numberOfSidesは宣言時に、nameは初期化時に値が入れられています。
オブジェクトが解放される前に、クリーンアップをしたいときはdeinitを使ってデイニシャライザを生成します。
サブクラス
サブクラスを作成するには、サブクラスしたいスーパークラスの名前をコロン(:)の後に記述します。クラスが標準ルートクラスをサブクラスしないといけないというルールはないので、必要に応じてスーパークラスを含めることができます。
//Shapeクラスをサブクラスする
class Square: Shape {
var sideLength: Double
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}
//スーパークラスの実装をオーバーライドしているのでoverrideをつける
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
サブクラスで、スーパークラスの実装をオーバーライドするメソッドにはoverrideを付けます。overrideなしで実装をオーバーライドしようとするとコンパイルエラーになります。
コンパイラはスーパークラスの実装をオーバーライドしていないのにoverrideとついているメソッドも検出するので、スーパークラスに記述のないメソッドに対してoverriceしようとするとエラーとなります。
プロパティは値を保持するだけでなく、ゲッターとセッターを持つこともできます。
(getterは値が取得されるときに呼ばれるメソッド、setterは値がセットされた時に呼ばれるメソッドです。)
class Triangle: Shape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var totalSideLength: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "\(name) with sides of length \(sideLength)."
}
}
//TriangleクラスをsideLength = 3.1, name = "a triangle"で初期化する
var triangle = Triangle(sideLength: 3.1, name: "a triangle")
//totalSideLengthのgetが実行される
print(triangle.totalSideLength)
//totalSideLengthのsetが実行されてsideLengthに新しい値が設定される
triangle.totalSideLength = 9.9
print(triangle.sideLength)
triangle.simpleDescription()
totalSideLengthのセッターの中で、新しい値にはnewValueという名前がついていることに注目して下さい。
setのあとの括弧の中で、値に新しい名前をつけることができます。
EquilateralTriangleクラスの初期化には3つのステップがあります。
1. サブクラスで宣言されたプロパティに値を代入する
2. スーパークラスのイニシャライザを呼ぶ
3. スーパークラスで定義されたプロパティの値を変更する。メソッド、ゲッター、セッターを使った追加のセットアップがあればこのタイミングで行う。
もしプロパティを計算する必要はないものの、新しい値が設定される前と後でコードを実行させたい場合には、willSetとdidSetを使います。コードはイニシャライザ以外の場所で値が変更された場合に実行されます。
例えば、下記のコードでは三角形の変の長さが正方形の辺の長さと必ず同じになるようにしています。
class TriangleAndSquare {
var triangle: Triangle {
willSet {
//triangleの値がセットされたらsquareのsideLengthに同じ値をセットする
square.sideLength = newValue.sideLength
}
}
var square: Square {
willSet {
//squareの値がセットされたらtriangleのsideLengthに同じ値をセットする
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = Triangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
オプショナル値
オプショナル値を扱っている場合は?をメソッドやプロパティ、サブスクリプティングの前に置くことができます。もし?の前の値がnilだった場合、?のあとのコードは実行されず、式全体の値がnilになります。?の前の値がnilでない場合は、オプショナル値はアンラップされ、アンラップされた値に対して?の後の処理が行われます。どちらのケースでも、式全体の値はオプショナル値です。
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
次回は【列挙型と構造体】について解説します!
今までのSwiftイントロダクション
#1 シンプルな値
#2 制御フロー
#3 関数とクロージャ