原文: Apple Inc. “The Swift Programming Language”。 iBooks. https://itun.es/jp/jEUH0.l
#Swiftツアー
その1からの続きです
オブジェクトとクラス
###クラス
- 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 NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
- コンストラクタ内にて、selfをつけることでプロパティのnameと引数のnameが区別されています
- すべてのプロパティには値を設定しなければいけません。numberOfSidesのように宣言時に設定するか、nameのようにコンストラクタで設定させます
###デストラクタ
- デストラクタにはdeinitをつかいます。
継承
- クラスの継承は、クラス名の後ろにコロンと継承するクラス名をつけます
- 基本的にクラスを記述するときは、標準基底クラスを継承する必要はありません。
- スーパークラスの関数をオーバーライド
するときは、overrideをつけます。 - overrideをつけずに関数をオーバーライドしようとするとエラーになります。また、スーパークラスがもっていない関数名に対してoverrideをつけた関数を定義してもエラーになります
class Square: NamedShape {
var sideLength: Double
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}
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()
###ゲッター・セッター
- プロパティにはゲッターとセッターを設定することができます
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "An equilateral triagle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength
-
上記のperimeterのセッターでは、受け取る値に暗黙的にnewValueという名前がつきます。明示的に名前をつけるときはsetの後ろに括弧をつけて名前を入れます
-
上記のEquilateralTriangleクラスのコンストラクタでは3種類のステップが行われています
1.サブクラスで定義されたプロパティに値を設定している
2.スーパークラスのコンストラクタを呼ぶ
3.スーパークラスで定義されているプロパティの値を変更する。関数やゲッター、セッターを使うその他の初期設定もこの時点で行うことができます
###willset・didset
- プロパティに値が設定された後または前に処理を行いたいときは、willsetとdidsetを使います
- 下記ではtriangleのsideLengthはsquareのsideLengthと常に同じになるようになっています
class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
square.sideLength = newValue.sideLength
}
}
var square: Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilateralTriangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength
- 関数とクラスのメソッドの違いとして、関数の引数名は関数内でしか使われませんが、メソッドの引数名(1つ目の引数以外)は関数を呼ぶときにも使用します。
- 基本的にメソッドの引数名はメソッド内で使う時も同じ名前で使用されますが、引数名に2つ目の名前をつけることができメソッド内で使用することができます。
class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes times: Int) {
count += amount * times
}
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)
optional値
- optional値(nilを含む値) への関数やプロパティへアクセスするときは?をつけます
- ?がついている値がnilの場合、その後ろの処理は無視され、nilが返ります。nilかそうでない場合においても、最後の結果もoptional値になります
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
##列挙体と構造体
###列挙体
- 列挙体を作るにはenumを使います
- クラスなどの様に列挙体もメソッドを持つことができます
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.toRaw())
}
}
}
let ace = Rank.Ace
let aceRawValue = ace.toRaw()
- 上記の例では、列挙体の定数値の型はIntなので、最初の定数値しか指定できません。その他の定数値は順番に自動的に割り当てられます。
- 定数値の型は他に文字列型や浮動小数型を指定することもできます
- toRawとfromRow関数は定数と定数値の変換を行います
if let convertedRank = Rank.fromRaw(3) {
let threeDescription = convertedRank.simpleDescription()
}
- 列挙体の定数は、定数値にタグづけする為だけのものではいので、必ずしも定数値を設定してあげなくてもいい。
enum Suit {
case Spades, Hearts, Diamonds, Clubs
func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()
- 上記の"let hearts"の箇所では、heartsの型が定まっていないため、Suit.Heartsのように列挙体の定数をフルネームで指定しています。
一方、switchの中では.Heartsと省力された形で呼ばれています。なぜならselfはsuitであることが自明であるためです。値の型がわかってれば省略した呼び出し方がいつでもできます。
###構造体
- structを使い構造体を作成します
- 構造体はクラスと同じようにメソッドやコンストラクタを持ちます
- 構造体とクラスの一番の違いは、構造体を呼び出すときは毎回コピーされた構造が渡されますが、クラスの場合は参照が渡されます
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
Associated値
- 列挙体はインスタンスに合わせた関連値(Associated値)を持つことが出来ます。同じ列挙体でもインスタンスごとに異なる関連値をもつことが出来ます。
- 関連値はインスタンスを作成するときに設定します
- 関連値と定数値は異なります。定数値は列挙体のすべてのインスタンスで同じ値になります。定数値の設定は列挙体を定義するときに行います
- 下記の例は、日の出と日の入りの時刻をサーバから問い合わせて、時刻を返すかエラーを返します
enum ServerResponse {
case Result(String, String)
case Error(String)
}
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
switch success {
case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
let serverResponse = "Failure... \(error)"
}
- 日の出と日の入りの時刻はswitchのcaseで一致したServerResponseから渡されています
その3へ続く