簡體   English   中英

scala中的類型安全的respond_to可能嗎?

[英]Is a type-safe respond_to in scala possible?

使用用於類型安全鑄造的案例構造很容易在scala中完成。 以下代碼確保僅在具有相應類型的對象上調用square

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")
}

另一方面,有些情況下,使用類型信息進行轉換並不容易,只需檢查{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")
}

使用隱式證據參數,可以根據其他方法的可用性定義方法。 是否也可以在定義它們時調用它們?

結構類型表示成員的非繼承約束可用性,因此如果您希望方法只接受帶有特定方法的值,例如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開始,反射信息在逐個類的基礎上緩存在調用站點上(實際提供的值類)。

似乎類型類似乎是將操作應用於許多不同類型的標准。 它不是在運行時查找方法(嗯,它可以,但不是純模式),但它可以提供你想要的。

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實際上提供了一個用於數值計算的數值類型,請參閱: http//www.scala-lang.org/api/current/scala/math/Numeric.html

我還寫了一篇關於Scala中類型類模式的文章,以及使用它來調整多個API或在此處執行多個調度的方法: http//suereth.blogspot.com/2010/07/monkey-patching-duck-typing-and -type.html

如果你谷歌搜索“scala類型”,你應該看到很多信息。

第2部分 - 實際的respond_to?

如果你真的想在scala中使用respond_to,那你就有點SOL了。 這是因為respond_to確實是一個動態的概念。 您正在定義一個類的方法,如果您嘗試在不存在的類上調用方法,則該方法將被調用。 Scala不像一些動態JVM語言那樣抽象方法調用。 這意味着沒有鈎子進入方法調用來攔截和交互。 您可以做的最好的是一種接口適配,或者某種面向方面的后編譯鈎子,為您重寫字節碼。

我們可以使用一個神奇的部分,它也用在一些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的ClassCastException到Foo,那么你知道你在類加載器地獄。

無論如何,如果您對動態代理有其他疑問,請隨時提出。 在靜態類型語言中,您可能最好使用類型類並使用它們遷移到設計模式而不是使用respond_to。 (您會對使用類型類和類型系統完成的工作感到驚訝)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM