見出し画像

オブジェクトとクラス【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."
   }
}

画像1

上記のコードで、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なしで実装をオーバーライドしようとするとコンパイルエラーになります。

画像2

コンパイラはスーパークラスの実装をオーバーライドしていないのにoverrideとついているメソッドも検出するので、スーパークラスに記述のないメソッドに対してoverriceしようとするとエラーとなります。

画像3


プロパティは値を保持するだけでなく、ゲッターとセッターを持つこともできます。

(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()

画像4


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 関数とクロージャ


この記事が気に入ったらサポートをしてみませんか?