Swift の class(クラス)

ここでは Swift の class(クラス)についてご説明します。

Swift の class の基本

Swift の class(クラス)では、struct と同様に、値を保持するためのプロパティや関連する機能のメソッドなどをまとめて定義しておいて、必要な時にその class のインスタンスを生成して使うことができます。

class と struct の構文はほぼ同じですが、class は継承できたり、デイニシャライズが定義できるなどの違いがあります。

プロパティの定義の箇所と init() でプロパティの初期値が設定でき、インスタンスが生成された時に初期化されていないプロパティーがあるとエラーになります。

struct ではイニシャライザー init() を定義しなくても、デフォルトのイニシャライザーが自動的に生成されているような感じで使え、エラーになりませんでしたが、class では、プロパティの初期値がない状態でイニシャライザーがないとエラーになってしまいます。

class のほうが多機能である分複雑で、Swift の公式ドキュメントでは特に必要がない限り struct を使うことがおすすめされています。


Swift の class(クラス)を定義するには、class キーワードを使って次のような構文で定義します。

class Class名 {
    var プロパティ名1: データ型1
    var プロパティ名2: データ型2

    init() {
        イニシャライザーのコードブロック
    }

    func メソッド名() {
        メソッドのコードブロック
    }
}

例えば、名前と年齢をプロパティに持ち、あいさつをする greet メソッドを持つ、Person という class を定義したい時は次のようにできます。

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

ちなみに、ディイニシャライザーを定義したい時は deinit { ... } のように定義します。


Swift の class の使い方

Swift の class のインスタンスを生成するには、基本的には次のようにできます。

var または let 変数名 = Class名(プロパティ名1: プロパティ名1の値, プロパティ名2: プロパティ名2の値, ...)

プロパティの値を変更したい時は、Class名.プロパティ名1 = 新しい値 のようにして変更できます。

そして、メソッドを実行する際は Class名.メソッド名() のように実行します。


それでは、先ほど定義した Person クラスを使ってみましょう。

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

let person1 = Person(name: "Ryota", age: 20)
person1.greet()

let person2 = Person(name: "Sayaka", age: 15)
person2.greet()

実行結果は次のようになります。

Hello! My name is Ryota, and I'm 20 years old.
Hello! My name is Sayaka, and I'm 15 years old.

Swift の class は参照型

もう一点、Swift の class と struct の違いは、class は参照型で struct は値型であるという点です。

例えば、次のように class をコピーして新しい class を生成し、新しく生成した class のプロパティーの値を変更すると、元の class のプロパティの値も変更されます。

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

var p1 = Person(name: "Haruka", age: 15)

var p2 = p1
p2.age = 20

p1.greet()
p2.greet()

実行結果は次のようになります。p2 の age を 20 に変更すると、p1 の age も 20 になっていますね。

Hello! My name is Haruka, and I'm 20 years old.
Hello! My name is Haruka, and I'm 20 years old.

このコードの class を struct に変更して実行すると、p1 と p2 は別のインスタンスになるので、p2 の age を 20 に変更しても、p1 の age は 15 のままになります。


また、struct では let でインスタンスを生成するとプロパティは変更できませんが、class では固定されているのは参照なので、プロパティの値が変更できます。

以下のように上のコードのインスタンスを生成する時の var を let に変更して実行してもエラーになりません。

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

let p1 = Person(name: "Haruka", age: 15)

let p2 = p1
p2.age = 20

p1.greet()
p2.greet()

実行結果は先ほどと同じです。

Hello! My name is Haruka, and I'm 20 years old.
Hello! My name is Haruka, and I'm 20 years old.

このコードの class を struct に変更して実行すると、p2 のプロパティの値を変更する箇所でエラーになります。

Swift の class(クラス) 1


Swift でクラスを継承して派生クラスを定義する

Swift でクラスを継承して派生クラスを定義するには、クラスを定義する時に class 派生クラス名 : 基底クラス名 のようにします。

また、派生クラスの init() メソッド内で、基底クラスの init() メソッドを呼ぶには super を使って、次のようにします。

class DerivedClassName: ParentClassName {
    init() {
      super.init()
   }
}

では、実際に Swift でクラスを継承して、派生クラスを定義して使ってみましょう。

上で定義した Person クラスを継承した Employee という派生クラスを作ります。

Employee クラスでは Person クラスが持つ name(名前)、age(年齢)に加えて、department(部署)というクラス変数を追加してみましょう。

Employee クラスの init() の引数には name, age, department を指定し、基底クラスの super.init() には name と age のみを渡します。

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

class Employee: Person {
    var department: String
    
    init(name: String, age: Int, department: String) {
        self.department = department
        super.init(name: name, age: age)
   }
}

let emp1 = Employee(name: "Haruka", age: 30, department: "Sales")

print(emp1.name)
print(emp1.age)
print(emp1.department)
emp1.greet()

実行結果は次のようになります。

Haruka
30
Sales
Hello! My name is Haruka, and I'm 30 years old.

department に加え、Person のクラスから継承した name、age 変数や greet() メソッドが利用できていますね。

Swift の派生クラスにメソッドを追加する

派生クラスの Employee にメソッドを追加してみましょう。

社員の有給休暇の日数を保持する paidVacationDays という名前のクラス変数を使って初期値を 20 日に設定しておきます。

そして、有給休暇を使う日数を渡して、有給休暇の日数が残っている場合は渡した日数を減らして残日数を表示、残っていなければ有給休暇の日数が足りないことを表示するような usePaidVacationDays() メソッドを追加し、使ってみましょう。

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

class Employee: Person {
    var department: String
    var paidVacationDays: Int = 20
    
    init(name: String, age: Int, department: String) {
        self.department = department
        super.init(name: name, age: age)
    }
    
    func usePaidVacationDays(days: Int) {
        if paidVacationDays >= days {
            paidVacationDays -= days
            print("有給休暇の残日数は \(paidVacationDays) 日です。")
        } else {
            print("有給休暇の日数が足りません。")
        }
    }
}

let emp1 = Employee(name: "Haruka", age: 30, department: "Sales")
emp1.usePaidVacationDays(days: 3)
emp1.usePaidVacationDays(days: 20)

実行結果は次のようになります。

有給休暇の残日数は 17 日です。
有給休暇の日数が足りません。

1 回目は 20 日から引数で渡した 3 を引いて、「有給休暇の残日数は 17 日です。」と出力されましたが、2 回目はもう 17 日しか残っていないのに 20 日分使おうとしたので、「有給休暇の日数が足りません。」と出力されています。


Swift の基底クラスのメソッドをオーバーライドする

基底クラスのメソッドが派生クラスでやりたいことに合わない場合は、基底クラスのメソッドをオーバーライドすることができます。

オーバーライドしたい時は派生クラスの中で override のキーワードを使って同じ名前でクラスメソッドを定義します。

そうすると、その名前のメソッドが実行された時に、基底クラスのメソッドではなく、派生クラスのメソッドが実行されます。


例えば、基底クラスの greet() メソッドをオーバーライドしたい時は次のようにします。

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() {
        print("Hello! My name is \(name), and I'm \(age) years old.")
    }
}

class Employee: Person {
    var department: String
    var paidVacationDays: Int = 20
    
    init(name: String, age: Int, department: String) {
        self.department = department
        super.init(name: name, age: age)
    }
    
    func usePaidVacationDays(days: Int) {
        if paidVacationDays >= days {
            paidVacationDays -= days
            print("有給休暇の残日数は \(paidVacationDays) 日です。")
        } else {
            print("有給休暇の日数が足りません。")
        }
    }
    
    override func greet() {
        print("Hello, I'm \(name). I work in the \(department.lowercased()) department.")
    }
}

let emp1 = Employee(name: "Haruka", age: 30, department: "Sales")
emp1.greet()

実行結果は次のようになり、基底クラスではなく派生クラスの greet() メソッドが実行されていますね。

Hello, I'm Haruka. I work in the sales department.

以上、Swift の class(クラス)についてご説明しました。

© 2024 iOS 開発入門