これは、質問が変更される前の古い答えです。
強く型付けされたプログラミング言語は、あなたがやろうとしていることを妨げます。理由を見てみましょう。
次のシグネチャを持つメソッドのアイデア:
def getIt( a:Base ) : Unit
メソッドの本体は、Baseクラスまたはインターフェイスを介して表示されるプロパティ、つまり、Baseクラス/インターフェイスまたはその親でのみ定義されたプロパティとメソッドにアクセスできるようになります。コードの実行中に、メソッドに渡される特定のインスタンスごとgetIt
に異なるサブクラスが存在する可能性がありますが、のコンパイルタイプa
は常に次のようになります。Base
このように推論することができます:
わかりました。クラスBaseがあり、2つのケースクラスで継承し、同じ名前のプロパティを追加してから、Baseのインスタンスのプロパティにアクセスしようとします。
簡単な例は、これが安全でない理由を示しています。
sealed abstract class Base
case class Foo(myparam:String) extends Base
case class Bar(myparam:String) extends Base
case class Evil(myEvilParam:String) extends Base
def getIt( a:Base ) = a.copy(myparam="changed")
次の場合、コンパイラがコンパイル時にエラーをスローしなかった場合、コードは実行時に存在しないプロパティにアクセスしようとすることを意味します。これは厳密に型指定されたプログラミング言語では不可能です。これにより、コードに含まれる可能性のあるバグの数が大幅に減少することを知っているため、コンパイラによるコードの検証を大幅に強化するために、記述できるコードに対する制限を交換しました。
これが新しい答えです。結論に達するまでに必要なポイントが少ないため、少し長いです
残念ながら、提案した内容を実装するために、ケースクラスのコピーのメカニズムに依存することはできません。copyメソッドが機能する方法は、大文字と小文字を区別しないクラスで自分で実装できるコピーコンストラクターです。ケースクラスを作成し、REPLで分解してみましょう。
scala> case class MyClass(name:String, surname:String, myJob:String)
defined class MyClass
scala> :javap MyClass
Compiled from "<console>"
public class MyClass extends java.lang.Object implements scala.ScalaObject,scala.Product,scala.Serializable{
public scala.collection.Iterator productIterator();
public scala.collection.Iterator productElements();
public java.lang.String name();
public java.lang.String surname();
public java.lang.String myJob();
public MyClass copy(java.lang.String, java.lang.String, java.lang.String);
public java.lang.String copy$default$3();
public java.lang.String copy$default$2();
public java.lang.String copy$default$1();
public int hashCode();
public java.lang.String toString();
public boolean equals(java.lang.Object);
public java.lang.String productPrefix();
public int productArity();
public java.lang.Object productElement(int);
public boolean canEqual(java.lang.Object);
public MyClass(java.lang.String, java.lang.String, java.lang.String);
}
Scalaでは、copyメソッドは3つのパラメーターを取り、最終的には現在のインスタンスから指定していないものを使用できます(Scala言語は、その機能の中でメソッド呼び出しのパラメーターのデフォルト値を提供します)
分析を進めて、更新されたコードをもう一度取得してみましょう。
sealed abstract class Base(val myparam:String)
case class Foo(override val myparam:String) extends Base(myparam)
case class Bar(override val myparam:String) extends Base(myparam)
def getIt( a:Base ) = a.copy(myparam="changed")
このコンパイルを行うには、次のコントラクトを尊重するgetIt(a:MyType)
の署名で使用する必要があります。MyType
パラメータmyparamと、デフォルト値を持つ他のパラメータを持つもの
これらすべての方法が適しています。
def copy(myParam:String) = null
def copy(myParam:String, myParam2:String="hello") = null
def copy(myParam:String,myParam2:Option[Option[Option[Double]]]=None) = null
この契約をScalaで表現する方法はありませんが、役立つ高度なテクニックがあります。
case classes
私たちができる最初の観察は、ScalaとScalaの間には厳密な関係があるということですtuples
。実際、ケースクラスは、追加の動作と名前付きプロパティを備えたタプルです。
2番目の観察結果は、クラス階層のプロパティの数が同じであることが保証されていないため、コピーメソッドのシグネチャが同じであるとは保証されていないということです。
AnyTuple[Int]
実際には、最初の値がInt型である任意のサイズを記述していると仮定するとTuple
、次のようなことを実行しようとしています。
def copyTupleChangingFirstElement(myParam:AnyTuple[Int], newValue:Int) = myParam.copy(_1=newValue)
すべての要素がである場合、これは難しくありませんInt
。同じタイプのすべての要素を持つタプルはでありList
、の最初の要素を置き換える方法を知っていList
ます。TupleX
anyをに変換しList
、最初の要素を置き換えて、List
backをに変換する必要がありTupleX
ます。X
はい、想定するすべての値に対してすべてのコンバーターを作成する必要があります。迷惑ですが、難しくはありません。
ただし、この場合、すべての要素がであるとは限りませんInt
。Tuple
最初の要素がIntの場合、要素のタイプが異なる場所をすべて同じであるかのように扱いたいと思います。これはと呼ばれます
「アリティを超えて抽象化」
つまり、サイズに関係なく、一般的な方法で異なるサイズのタプルを処理します。そのためには、それらを異種タイプをサポートする特別なリストに変換する必要があります。HList
結論
メーリングリストの複数の投稿からわかるように、ケースクラスの継承は非常に正当な理由で非推奨になっています:http ://www.scala-lang.org/node/3289
問題に対処するための2つの戦略があります。
変更する必要のあるフィールドの数が限られている場合は、@Ronによって提案されたようなコピーメソッドを持つアプローチを使用してください。型情報を失わずにやりたいのなら、基本クラスの生成に行きます
sealed abstract class Base[T](val param:String){
def copy(param:String):T
}
class Foo(param:String) extends Base[Foo](param){
def copy(param: String) = new Foo(param)
}
def getIt[T](a:Base[T]) : T = a.copy("hello")
scala> new Foo("Pippo")
res0: Foo = Foo@4ab8fba5
scala> getIt(res0)
res1: Foo = Foo@5b927504
scala> res1.param
res2: String = hello
本当にアリティを抽象化したい場合、解決策は、MilesSabinによって開発されたShapelessというライブラリを使用することです。議論の後に尋ねられた質問がここにあります:HListsはタプルを書く複雑な方法にすぎませんか?しかし、これはあなたにいくつかの頭痛を与えるだろうとあなたに言います