いよいよ正式リリースが近づいてきたScala 2.13ですが、既存のコードをリリース候補版のScala 2.13でコンパイルすると以下のようなメッセージを見かけるようになりました。
scala> def makeSeq(a: Array[Int]): Seq[Int] = if (a == null) Nil else a ^ warning: method copyArrayToImmutableIndexedSeq in class LowPriorityImplicits2 is deprecated (since 2.13.0): Implicit conversions from Array to immutable.IndexedSeq are implemented by copying; Use the more efficient non-copying ArraySeq.unsafeWrapArray or an explicit toIndexedSeq call makeSeq: (a: Array[Int])Seq[Int]
copyArrayToImmutableIndexedSeq
なんてメソッド、呼び出していたっけ?LowPriorityImplicits2
っていうクラスって何だっけ?そもそも、このメッセージは何を意味しているのか?…調べた結果をまとめました。
まずはScala 2.13へのマイグレーションガイドをチェックします。
20ページに関連する記載が有ります。
Wrapped Java varargs pretend the array is immutable • Using the new s.c.i.ArraySeq • You can do the same with ArraySeq.unsafeWrapArray • A deprecated implicit conversion makes a copy of the array
Scala 2.12までは、ArrayをSeqへ渡すと、scala.collection.mutable.WrappedArray
に変換されていました。これはScala 2.12のscala.Predef
で定義されているImplicit Conversionの挙動です。
scala> val r: Seq[Int] = Array(1, 2) r: Seq[Int] = WrappedArray(1, 2)
Scala 2.13ではSeqがImmutable化されたことを受けて、scala.collection.immutable.ArraySeq
に変換されます。
scala> val r: Seq[Int] = Array(1, 2) ^ warning: method copyArrayToImmutableIndexedSeq in class LowPriorityImplicits2 is deprecated (since 2.13.0): Implicit conversions from Array to immutable.IndexedSeq are implemented by copying; Use the more efficient non-copying ArraySeq.unsafeWrapArray or an explicit toIndexedSeq call r: Seq[Int] = ArraySeq(1, 2)
これはScala 2.13のscala.Predef
で定義が追加された以下のコードによるものです。
private[scala] abstract class LowPriorityImplicits2 { @deprecated("Implicit conversions from Array to immutable.IndexedSeq are implemented by copying; Use the more efficient non-copying ArraySeq.unsafeWrapArray or an explicit toIndexedSeq call", "2.13.0") implicit def copyArrayToImmutableIndexedSeq[T](xs: Array[T]): IndexedSeq[T] = if (xs eq null) null else new ArrayOps(xs).toIndexedSeq }
LowPriorityImplicits2
もcopyArrayToImmutableIndexedSeq
が出てきました。暗黙の変換によりコピーが発生するので、toIndexedSeq
でコピーして変換するか、それではパフォーマンス的に問題になる場合はArraySeq.unsafeWrapArray
を使ってコピー無しにimmutable化するか明示的に示しましょう、ということですね「ArraySeq.unsafeWrapArray
」って何がunsafe
なの?という話は、また別のエントリを書きます)。
確かに、警告が出なくなります。
scala> val r1: Seq[Int] = Array(1, 2).toIndexedSeq r1: Seq[Int] = ArraySeq(1, 2) scala> val r2: Seq[Int] = scala.collection.immutable.ArraySeq.unsafeWrapArray(Array(1, 2)) r2: Seq[Int] = ArraySeq(1, 2)
しかし、元々LowPriorityImplicits2
というクラスも、copyArrayToImmutableIndexedSeq
というメソッドも、Scala 2.12まではなかったし、Scala 2.13でも明示的に使うものではありません(implicit conversionですしね)。
定義された瞬間に非推奨になってしまうクラスと、メソッド…しかもユーザーコード上には出てきません…「実は、最初からそんなメソッドなんか無かったんですよ…」って言うと何だか押井守っぽいですね。
というわけで上記のメッセージが出たら、明示的に変換方法を指定しましょう、という話でした。