KotlinとSwiftの拡張比較 (Swift 3.0対応)

2016.11.08

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

まえがき

拡張の便利さを一度経験すると拡張なしでは生きていけないくらい、中毒性の高い機能です。
KotlinでAndroid開発していると、そのありがたさ、便利さをしみじみ感じます。
Swiftでも同じく拡張機能あったのでやってみました。

そもそも拡張って

このクラスにこんな機能があったらいいのに。っと思った時、以下の2つができると思います。

  • クラスを継承して、サブクラスでメソッドを追加する
  • Utilクラスを作り、staticメソッドを定義し、static importでメソッド呼び出し。

しかし、めんどくさいですよね。
それを簡単に作れる機能、それが拡張です!

簡単な拡張

Stringにlogメソッドを拡張して、printで表示するようにしてみましょう。

Kotlin

fun String.log() {
    print(this)
}
"test".log()

String. でStringに拡張することを宣言しています。

Swift

extension String {
    func log() {
        print(self)
    }
}
"test".log()

extensionでどれに拡張するか宣言し、以下に拡張メソッドを定義します

Genericsの拡張

具体的クラスの拡張は簡単でした。
Genericsの拡張をする場合はどうしたらよいでしょうか?
例えば、先程のlog()をすべてのクラスで使用できるように拡張する例でみてみます。

Kotlin

//Swiftに名前を合わせている.protocolの代わりにinterfaceを使う
interface LoggableProtocol {
    fun string(): String

    //デフォルトの実装を定義できる
    //interfaceのメソッドを拡張で実装することはできない
    fun log() = print(string())
}

//適応例
class Person(val name: String, val age: Int) : LoggableProtocol {
    override fun string() = "name: $name , age: $age"
}

val person = Person("kamedon", 20)
// name: kamedon , age: 20
person.log()

//Personのlogをextensionしてみると
fun Person.log() {
    print("test")
}

// 上書きはされない
// name: kamedon , age: 20
person.log()

※toStringはJavaにはありますが、Swiftにはないようです。そのため、Kotlinも toStringがないこと前提に書いています。

余談:Genericsの拡張とtoStringがすべてクラスで使えるので、これでできます。

fun <T> T.log() {
    print(toString())
}
  • Kotlinの場合は、すでに定義されているメソッドや変数の拡張はできない。
    • interfaceで定義してあるメソッドに拡張不可
  • 変更する場合は、interfaceを敬称してるクラスでoverrideする必要がある。
  • Genericsの拡張ができる

Swift

// 1.拡張したいメソッドをprotocolで記述する
protocol LoggableProtocol {
    func string() -> String
    func log()
}

// 2.protocolには実装はかけないため、extensionで実装する
extension LoggableProtocol {
    func log() {
        print(string())
    }

}

// 3.実際に使用したいクラスに敬称させる
class Person: LoggableProtocol {
    func string() -> String {
        return "name: \(name) , age: \(age)"
    }

    let name: String
    let age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

//使用例

let person = Person(name: "kamedon", age: 20)
// name: kamedon , age: 20
person.log()

//Personのlogをextensionしてみると
extension Person {
    func log() {
        print("test")
    }
}

//test
person.log()
  • Swiftはすでに定義されているメソッドも拡張で上書きすることできる
  • protocolに実装を記述することはできないため、extensionで実装する
  • Genericsの拡張を直接することができない

まとめ

interface と protocol, 拡張は似ていますが、できることが違います。
kotlinの拡張をSwiftで実現しようとすると詰まりやすいです。
protocolで定義、extensionで実装しないといけないケースがあり、少しややこしい印象です。
今回はメソッドのみ拡張しましたが、変数も拡張できますので、遊んでみてくださいね

最後に、拡張はオレオレ機能、オレオレ流儀が乱立し、何がなんだかわからなくなるときがあります。。。
用法用量を守って正しくお使いください。

動作環境

  • kotlin: 1.0.4
  • Swift: 3.0.1

参考