型クラスは、操作を多くの異なる型に適用するための標準のようです。実行時にメソッドを検索するわけではありませんが (もちろんできますが、純粋なパターンではありません)、必要なものを提供できます。
trait Numeric[T] {
def times(x :T, y : T) : T
}
object Numeric {
implicit val doubleNumeric = new Numeric[Double] {
def times(x : Double, y : Double) = x*y
}
implicit val intNumeric = new Numeric[Int] {
def times(x : Int, y : Int) = x*y
}
}
def square[A : Numeric](x : A) = implicitly[Numeric[A]].times(x,x)
scala REPL でこれを行っている場合は、オブジェクト Numeric が、特性 Numeric の真のコンパニオン オブジェクトであることを確認してください。これを行うには、宣言を別のオブジェクト (tmp など) でラップし、tmp._ をインポートします。
次に、異なる値で square を呼び出すだけです:
scala> square(2)
res6: Int = 4
scala> square(4.0)
res7: Double = 16.0
Scala は、数値計算で使用する Numeric 型クラスを実際に提供しています。
また、Scala の型クラス パターンと、それを使用して複数の API を適応させたり、複数のディスパッチを行ったりする方法に関する記事をここに書きました:
http://suereth.blogspot.com/2010/07/monkey-patching-duck-typing-and -type.html
「scala type class」をググると、たくさんの情報が表示されるはずです。
パート #2 - 実際の Respond_to?
本当に scala で Respond_to が必要な場合は、やや SOL です。これは、respond_to が動的な概念であるためです。存在しないクラスのメソッドを呼び出そうとすると呼び出されるクラスのメソッドを定義しています。Scala は、一部の動的 JVM 言語のようにメソッド呼び出しを抽象化しません。これは、傍受して対話するためのメソッド呼び出しへのフックがないことを意味します。あなたができる最善の方法は、インターフェースの適応の形式、またはバイトコードを書き換えるためのある種のアスペクト指向のコンパイル後のフックです。
利用できる魔法のピースが 1 つあります。これは、一部の AOP フレームワークでも使用されています: 動的プロキシです。
scala> object AllPowerfulProxy extends InvocationHandler {
| def invoke(proxy : AnyRef, m : Method, args : Array[AnyRef]) : AnyRef = {
| println(" You really want to call " + m.getName + "?")
| null // Maliciously Evil!
| }
| }
defined module AllPowerfulProxy
scala> def spawn[A : Manifest] : A = {
| val mf = implicitly[Manifest[A]]
| java.lang.reflect.Proxy.newProxyInstance(mf.erasure.getClassLoader,
| Array(mf.erasure),
| AllPowerfulProxy).asInstanceOf[A]
| }
spawn: [A](implicit evidence$1: Manifest[A])A
これを使用して、任意のインターフェイスにオブジェクトを生成できます。何ができるか見てみましょう:
scala> val x = spawn[TestInterface]
You really want to call toString?
java.lang.NullPointerException
at scala.runtime.ScalaRunTime$.stringOf(ScalaRunTime.scala:259)
私たちがそこで何をしたか分かりますか?REPL が式の結果で toString を呼び出そうとすると、動的プロキシで呼び出されます。プロキシはプレースホルダーであるため、実際の呼び出しは AllPowerfulProxy クラスに委譲され、「toString を本当に呼び出したいですか?」というメッセージが出力されます。その後、REPL は null を返し、例外をスローします。動的プロキシを使用するとエラーが実行時に移動するため、オブジェクトのインスタンス化と正しい型の返しには十分注意する必要があります。システムの複雑さに応じて、classLoaders についても考慮する必要があります。Foo から Foo への ClassCastException が発生した場合は、クラス ローダーの地獄にいることがわかります。
いずれにせよ、動的プロキシについて他に質問がある場合は、お気軽にお問い合わせください。静的に型付けされた言語では、型クラスを利用し、respond_to を使用するパターンではなく、型クラスを使用するデザイン パターンに移行する方がよいでしょう。(型クラスと型システムで達成できることに驚かれることでしょう)。