SwiftとKotlinの構文比較 (2) 〜クラス、列挙体、構造体、プロトコル、拡張

この記事は私のような「Kotlinが気になっているSwiftプログラマー」向けです。
Apple公式のSwiftチュートリアル [A Swift Tour](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html#//apple_ref/doc/uid/TP40014097-CH2-ID1) を、Kotlinで書き替えてみました。

第二回のトピックはこちらです。
　・クラス
　・列挙体
　・構造体
　・プロトコル（インターフェイス）
　・拡張

前回の記事をまだお読みでなければ、こちらもぜひ。
[SwiftとKotlinの構文比較 (1) 〜シンプルな値、制御フロー、関数とクロージャー](https://qiita.com/y-some/items/48371c650eea66cfd6fd)

# クラス

## コンストラクタ

```swift:Swift
class NamedShape {
    var numberOfSides: Int = 0
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}
```

```kotlin:Kotlin
class NamedShape(name: String) {
    var numberOfSides: Int = 0
    var name: String = ""
    
    init {
        this.name = name
    }
    
    fun simpleDescription() : String {
        return "A shape with ${numberOfSides} sides."
    }
}
```

Kotlinは上のようなプライマリー・コンストラクタ宣言と、セカンダリー・コンストラクタ宣言で記法が異なります。
セカンダリー・コンストラクタは`constructor(引数名: 型名)`という構文で宣言します。
ちょっと独特な感じがします。
[Kotlin Refrence -Classes](https://kotlinlang.org/docs/reference/classes.html#classes)

## 継承

```swift:Swift
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")
print(test.area()) // 27.04
print(test.simpleDescription()) // A square with sides of length 5.2.
```

```kotlin:Kotlin
class Square(sideLength: Double, name: String): NamedShape(name) {
    var sideLength: Double
    
    init {
        this.sideLength = sideLength
        numberOfSides = 4
    }
    
    fun area() : Double {
        return sideLength * sideLength
    }
    
    override fun simpleDescription() : String {
        return "A square with sides of length ${sideLength}."
    }
}

fun main(args: Array<String>) {
    val test = Square(sideLength = 5.2, name = "my test square")
    println(test.area()) // 27.0400000000000003
    println(test.simpleDescription()) // A square with sides of length 5.2.
}
```

Kotlinはスーパークラスで`open class NamedShape(name: String)`というように明示的に`open`宣言をしないと、継承やoverrideができません。
保守性を向上するための仕様でしょうね。なるほどな〜と思いました。

自分自身のインスタンスを示すキーワードは`this`です。

## プロパティ

SwiftとKotlinのプロパティ構文は良く似ています。

```swift:Swift
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 triangle with sides of length \(sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter) // 9.3
triangle.perimeter = 9.9
print(triangle.sideLength) // 3.3
```

```kotlin:Kotlin
class EquilateralTriangle(sideLength: Double, name: String): NamedShape(name) {
    var sideLength: Double = 0.0
    
    init {
        this.sideLength = sideLength
        numberOfSides = 3
    }
    
    var perimeter: Double
        get() {
            return 3.0 * sideLength
        }
        set(newValue) {
            sideLength = newValue / 3.0
        }
    
    override fun simpleDescription() : String {
        return "An equilateral triangle with sides of length ${sideLength}."
    }
}

fun main(args: Array<String>) {
    var triangle = EquilateralTriangle(sideLength = 3.1, name = "a triangle")
    println(triangle.perimeter) // 9.3
    triangle.perimeter = 9.9
    println(triangle.sideLength) // 3.3000000000000003
}
```

Kotlinはクラスに（Javaのような）フィールドを持つことはできません。
プロパティにバッキングフィールドが必要な場合、`field`という変数名で自動的にそれを提供してくれます。

```kotlin:Kotlin
var counter = 0 // イニシャライザの value はバッキングフィールドへ直に書き込まれる
  set(value) {
    if (value >= 0)
      field = value
  }
```

[Kotlin Reference -Properties and Fields](https://kotlinlang.org/docs/reference/properties.html)

# 列挙体

```swift:Swift
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.rawValue)
        }
    }
}
let ace = Rank.ace
print(ace.rawValue) // 1
print(ace.simpleDescription()) // ace
```

```kotlin:Kotlin
enum class Rank(val rawValue: Int) {
    ace(1), 
    two(2), three(3), four(4), five(5), six(6), seven(7), eight(8), nine(9), ten(10),
    jack(11), queen(12), king(13);
    fun simpleDescription() : String {
        when(this) {
            ace ->
                return "ace"
            jack ->
                return "jack"
            queen ->
                return "queen"
            king ->
                return "king"
            else ->
                return rawValue.toString()
        }
    }
}

fun main(args: Array<String>) {
    val ace = Rank.ace
    println(ace.rawValue) // 1
    println(ace.simpleDescription()) // ace
}
```

SwiftはInt、Kotlinはクラスを実体としているということで、少々違いがあります。
Kotlinはenum定数にnameプロパティを持っており、例えば上のコードの`return "ace"`を`return ace.name`としても同じ結果を得られます。
さらには、以下のようにも書けます。

```kotlin:Kotlin
        when(this) {
            ace, jack, queen, king ->
                return this.name
            else ->
                return rawValue.toString()
        }
```

# 構造体

```swift:Swift
struct Card {
    var rank: Rank
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .three)
print(threeOfSpades.simpleDescription()) // The 3
```

```kotlin:Kotlin
data class Card(var rank: Rank) {
    fun simpleDescription() : String {
        return "The ${rank.simpleDescription()}"
    }
}

fun main(args: Array<String>) {
    val threeOfSpades = Card(rank = Rank.three)
    println(threeOfSpades.simpleDescription()) // The 3
}
```

Kotlinはclassに`data`修飾子をつけます。

# プロトコル（インターフェイス）

```swift:Swift
protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}
```

```kotlin:Kotlin
interface ExampleProtocol {
    var simpleDescription: String
    fun adjust()
}
```

Kotlinのインターフェイスも実装（デフォルト実装）を持てます。

# 拡張

```swift:Swift
extension Int {
    var simpleDescription: String {
        return "The number \(self)"
    }
}
print(7.simpleDescription) // The number 7
```

```kotlin:Kotlin
val Int.simpleDescription: String get() = "The number ${this}"

fun main(args: Array<String>) {
	println(7.simpleDescription) // The number 7
}
```

Swiftはクラスを拡張、Kotlinはメソッドやプロパティ単位で拡張します。
その関係上、Swiftでの`extension クラス: プロトコル`と同等の設計はKotlinではできません。

# おわりに

SwiftとKotlinの構文は良く似ていて、どちらもシンプルでセーフティですよね。
どちらも書いていて楽しい言語だと感じます。
唯一の欠点はJavaを書くのが辛くなってしまうことです（笑）

なお、例外処理については、概念的な違いの理解が必要であり、構文比較だけでは意味がないと思われたため割愛しました。

## 参考リンク

[Swift is NOT like Kotlin..?](http://blog.applibot.co.jp/blog/2017/04/03/swift-is-not-like-kotlin/)