4

タイプ セーフなキャストにケース構造を使用することは、scala で簡単に実行できます。次のコードはsquare、対応する型を持つオブジェクトでのみ gets が呼び出されるようにします。

class O
class A extends O {
    def square(i: Int):Int = { i*i }
}
class B extends O {
    def square(d: Double):Double = { d*d }
}
class C extends O {}

def square(o: O) = o match {
    case a:A => print(a.square(3))
    case b:B => print(b.square(3.0))
    case c:C => print(9)
    case _ => print("9")
}

一方、型情報をキャストに使用するのは簡単ではなく、 a の可用性を確認するだけ{def square(Int): Int}で十分な場合もあります。似たようなことを可能にするscalaの構造はありますか

def square(o: O) = o match {
    case a:{def square(Int):Int} => print(a.square(3))
    case b:{def square(Double):Double} => print(b.square(3.0))
    case _ => print("9")
}

暗黙の証拠パラメータを使用すると、他のメソッドの可用性に応じてメソッドを定義できます。それらが定義されている場合にのみ呼び出すことも可能ですか?

4

2 に答える 2

5

構造型付けは、継承の制約を受けないメンバーの可用性を表現するため、特定のメソッドを保持する値のみをメソッドに受け入れさせたい場合、たとえばdef square(i: Int): Int、次の表記法を使用します。

class Squaring {
  type Squarable = { def square(i: Int): Int }
  def squareMe(s: Squarable): Int = s.square(17)
}

class CanSquare { def square(i: Int) = i * i }

val cs1 = new CanSquare
val s1 = new Squaring

printf("s1.squareMe(cs1)=%d%n", s1.squareMe(cs1))


s1.squareMe(cs1)=289

構造型付けはリフレクションを介して実装されることを知っておく必要がありますが、Scala 2.8 の時点では、リフレクション情報はクラスごと (提供された値の実際のクラス) で呼び出しサイトにキャッシュされます。

于 2010-08-08T14:42:15.603 に答える
1

型クラスは、操作を多くの異なる型に適用するための標準のようです。実行時にメソッドを検索するわけではありませんが (もちろんできますが、純粋なパターンではありません)、必要なものを提供できます。

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 を使用するパターンではなく、型クラスを使用するデザイン パターンに移行する方がよいでしょう。(型クラスと型システムで達成できることに驚かれることでしょう)。

于 2010-08-10T01:36:14.267 に答える