リフレクションに頼る場合は、名義型と構造型を組み合わせて使用できます。
import scala.language.reflectiveCalls
// A structural type exploiting the fact that all tuples have a _1 field
type IntComponent = { val _1: Int }
def add(list: List[Product with IntComponent], item: Product with IntComponent): List[Product with IntComponent] = {
if(list.isEmpty || list.head._1 > item._1) item :: list
else add(list.tail, item)
}
組み合わせProduct with IntComponent
は厳密には必要ありませんが、IntComponent
十分です。ただし、この組み合わせにより安全性が少し高まり、Product
必要に応じて によって宣言されたフィールドとメソッドを使用できるようになります。
これにより、かなりの静的型安全性が得られます。
add(Nil, (1, "foo", null)) // ok
add(Nil, ("foo", 0)) // error
// error: type mismatch;
// found : String("foo")
// required: Int
add(List[Product with IntComponent](Tuple1(1), Tuple2(-1, true)), (0, "yes"))
// Explicit type annotation required, error otherwise
// found : List[Product with Serializable]
// required: List[Product with this.IntComponent]
このアプローチの明らかな弱点は、Product with IntComponent
たとえば、 である任意のオブジェクトに忍び込むことができることです。
case class FakeTupleA(_1: Int)
add(Nil, FakeTupleA(99)) // ok
要件がなければ、Product
以下も機能します。
add(Nil, new FakeTupleB(99))
// error: type mismatch;
// found : this.FakeTupleB
// required: Product with this.IntComponent