gourlaysama が既に述べたようにProduct
、構文的に言えば、varargs を 1 つに変えるとうまくいきます。
implicit def fromInts(t: Product) = Values()
これにより、次の呼び出しを正常にコンパイルできます。
Foo.bar(1,2,3)
これは、コンパイラが自動的に 3 つの引数を にリフトするためTuple3[Int, Int, Int]
です。これは、アリティが 22 までの任意の数の引数で機能します。問題は、どのようにタイプ セーフにするかです。Product.productIterator
メソッド本体内の引数リストを取得する唯一の方法ですが、Iterator[Any]
. メソッドが s のみで呼び出されるという保証はありませんInt
。これは驚くべきことではありません。署名でInt
s のみが必要であると実際に言及したことさえないからです。
Product
制約のないリストと vararg リストの主な違いは、後者の場合、各要素が同じ型であるということです。型クラスを使用してこれをエンコードできます。
abstract sealed class IsVarArgsOf[P, E]
object IsVarArgsOf {
implicit def Tuple2[E]: IsVarArgsOf[(E, E), E] = null
implicit def Tuple3[E]: IsVarArgsOf[(E, E, E), E] = null
implicit def Tuple4[E]: IsVarArgsOf[(E, E, E, E), E] = null
implicit def Tuple5[E]: IsVarArgsOf[(E, E, E, E, E), E] = null
implicit def Tuple6[E]: IsVarArgsOf[(E, E, E, E, E), E] = null
// ... and so on... yes this is verbose, but can be done once for all
}
implicit class RichProduct[P]( val product: P ) {
def args[E]( implicit evidence: P IsVarArgsOf E ): Iterator[E] = {
// NOTE: by construction, those casts are safe and cannot fail
product.asInstanceOf[Product].productIterator.asInstanceOf[Iterator[E]]
}
}
case class Values( xs: Seq[Int] )
object Values {
implicit def fromInt( x : Int ) = Values( Seq( x ) )
implicit def fromInts[P]( xs: P )( implicit evidence: P IsVarArgsOf Int ) = Values( xs.args.toSeq )
}
object Foo {
def bar(values: Values) {}
}
Foo.bar(0)
Foo.bar(1,2,3)
メソッド署名フォームを変更しました
implicit def fromInts(t: Product)
に:
implicit def fromInts[P]( xs: P )( implicit evidence: P IsVarArgsOf Int )
メソッド本体の中で、 new methodd を使用しargs
て引数リストを取得します。
bar
s のタプルではないタプルで呼び出そうとするとInt
、コンパイル エラーが発生し、タイプ セーフが返されることに注意してください。
更新: 0__ で指摘されているように、上記のソリューションは数値の拡大ではうまく機能しません。つまり、次のコードはコンパイルされませんbar
が、単純に 3 つのInt
パラメーターを使用する場合は機能します。
Foo.bar(1:Short,2:Short,3:Short)
Foo.bar(1:Short,2:Byte,3:Int)
これを修正するにはIsVarArgsOf
、すべての暗黙要素がタプル要素をすべて同じ型にするのではなく、共通の型に変換できるように変更するだけです。
abstract sealed class IsVarArgsOf[P, E]
object IsVarArgsOf {
implicit def Tuple2[E,X1<%E,X2<%E]: IsVarArgsOf[(X1, X2), E] = null
implicit def Tuple3[E,X1<%E,X2<%E,X3<%E]: IsVarArgsOf[(X1, X2, X3), E] = null
implicit def Tuple4[E,X1<%E,X2<%E,X3<%E,X4<%E]: IsVarArgsOf[(X1, X2, X3, X4), E] = null
// ... and so on ...
}
わかりました、実は嘘をつきました、まだ終わっていません。現在、さまざまなタイプの要素を受け入れているため (それらが共通のタイプに変換可能である限り、それらを期待されるタイプにキャストすることはできません (これはランタイム キャスト エラーにつながります)、代わりに暗黙的な変換を適用する必要があります)。次のように作り直すことができます。
abstract sealed class IsVarArgsOf[P, E] {
def args( p: P ): Iterator[E]
}; object IsVarArgsOf {
implicit def Tuple2[E,X1<%E,X2<%E] = new IsVarArgsOf[(X1, X2), E]{
def args( p: (X1, X2) ) = Iterator[E](p._1, p._2)
}
implicit def Tuple3[E,X1<%E,X2<%E,X3<%E] = new IsVarArgsOf[(X1, X2, X3), E]{
def args( p: (X1, X2, X3) ) = Iterator[E](p._1, p._2, p._3)
}
implicit def Tuple4[E,X1<%E,X2<%E,X3<%E,X4<%E] = new IsVarArgsOf[(X1, X2, X3, X4), E]{
def args( p: (X1, X2, X3, X4) ) = Iterator[E](p._1, p._2, p._3, p._4)
}
// ... and so on ...
}
implicit class RichProduct[P]( val product: P ) {
def args[E]( implicit isVarArg: P IsVarArgsOf E ): Iterator[E] = {
isVarArg.args( product )
}
}
これにより、数値の拡大に関する問題が修正され、関係のない型が混在している場合でもコンパイルが行われます。
scala> Foo.bar(1,2,"three")
<console>:22: error: too many arguments for method bar: (values: Values)Unit
Foo.bar(1,2,"three")
^