Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

この記事は私のような「Kotlinが気になっているSwiftプログラマー」向けです。
Apple公式のSwiftチュートリアル A Swift Tour を、Kotlinで書き替えてみました。

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

前回の記事をまだお読みでなければ、こちらもぜひ。
SwiftとKotlinの構文比較 (1) 〜シンプルな値、制御フロー、関数とクロージャー

クラス

コンストラクタ

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

継承

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
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
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
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
var counter = 0 // イニシャライザの value はバッキングフィールドへ直に書き込まれる
  set(value) {
    if (value >= 0)
      field = value
  }

Kotlin Reference -Properties and Fields

列挙体

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
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
        when(this) {
            ace, jack, queen, king ->
                return this.name
            else ->
                return rawValue.toString()
        }

構造体

Swift
struct Card {
    var rank: Rank
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .three)
print(threeOfSpades.simpleDescription()) // The 3
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
protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}
Kotlin
interface ExampleProtocol {
    var simpleDescription: String
    fun adjust()
}

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

拡張

Swift
extension Int {
    var simpleDescription: String {
        return "The number \(self)"
    }
}
print(7.simpleDescription) // The number 7
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..?

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした