LoginSignup
4
2

More than 3 years have passed since last update.

Swiftで、Kotlinのdata class の copy() みたいのが欲しい

Last updated at Posted at 2020-04-27

Swift と Kotlin を行ったり来たりして開発していると、うーってなることが結構あります。

そんな中で、似ていて違ってうわってなるのが Kotlin の data class で暗黙に生成される copy() メソッドが Swift では生成されないことです。

ただ、コメントで指摘を受けて気付きましたが、Swift的には data class 的に struct を使用する時は、プロパティは var で定義すれば、 let が伝播してくので不変になりますね。

そうなるとこの実装の役立つタイミングは、変更できない、例えばライブラリやモジュールの構造体を扱いたい時とかの限定的な場面だけになりそうです。

実装してみたところ、結構うまくいったので、共有します。

実装したいことは、「引数で指定したプロパティだけ変更して、他の項目は元と同じ別の struct を入手したい」です。

struct Bean {
     let weight: Double
     let size: Double?
     let name: String?
 }

 extension Bean {
     func copy(
         weight: (() -> Double)? = nil,
         size: (() -> Double?)? = nil,
         name: (() -> String?)? = nil
     ) -> Bean {
         Bean(
             weight: choose(weight, value: self.weight),
             size: choose(size, value: self.size),
             name: choose(name, value: self.name)
         )
     }
 }

 func choose<T>(_ block: (() -> T)?, value: T) -> T {
     guard let block = block else { return value }
     return block()
 }

copy() メソッドの引数をOptionalのブロックにするのが思いつきです。
こちらの構造体は機械的で実装するのはまあまあ面倒ですが、使う時は例えば、

let blackSesame = Bean(weight: 0.1, size: 2.0, name: "黒ごま")
let whiteSesame = blackSesame.copy(name:{ "白ごま" })

こんな感じです。

extention はこんな実装もできそうです。どちらがいいかしら?

extension Bean {
    func copy(
        weight: (() -> Double)? = nil,
        size: (() -> Double?)? = nil,
        name: (() -> String?)? = nil
    ) -> Bean {
        Bean(
            weight: (weight ?? { self.weight })(),
            size: (size ?? { self.size })(),
            name: (name ?? { self.name })()
        )
    }
}

もっといい方法あったら、是非共有くださいませ。

4
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2