2012-02-07 64 views
10

我的目標是使用特性混合來增強內部scala代碼現有的Java類。例如,要向java.awt.geom.Ellipse2D類添加一個像java.awt.Rectangle.translate(dx,dy)這樣的方法。爲此,我創建了以下特點:構建一個橢圓時使用traits增強java類,如何在java字段內部trait中聲明?

trait RectangleLike { 
    var x: Double // abstract vals to correspond to java class fields 
    var y: Double // I need these vars to refer to them inside translate method 
    def translate(dx: Double, dy: Double) { 
    x = x + dx 
    y = y + dy 
    } 
    // more concrete trait methods here 
} // defines without errors in scala REPL 

然後使用特點:

val egg = new java.awt.geom.Ellipse2D.Double(5, 10, 20, 30) with RectangleLike 

然而,當我執行在斯卡拉REPL上面的腳本中,我得到以下輸出:

<console>:8: error: overriding variable x in trait RectangleLike of type Double; 
variable x in class Double of type Double has incompatible type; 
other members with override errors are: y 
val egg = new java.awt.geom.Ellipse2D.Double(5, 10, 20, 30) with RectangleLike 

我懷疑這個錯誤是由於斯卡拉實現變量的方式 - 作爲私有字段和getter/setter方法對。我試圖實現的是什麼?有沒有另外一種方法來定義trait中的java類字段,然後在具體的trait方法中引用它們?

在此先感謝 傑克·迪馬斯

回答

8

是的,這是可行的,但不是試圖訪問你想(這是最有可能是不好的想法),你會混在類的私有字段想要宣稱RectangleLike的自我類型爲java.awt.geom.RectangularShape,以便您可以使用您的特質Ellipse2D.Double以及Rectangle2D.Double

這裏是它如何工作的:

trait RectangleLike { 
    self: java.awt.geom.RectangularShape => 

    def translate(dx: Double, dy: Double) { 
    setFrame(getX + dx, getY + dy, getX + getWidth, getY + getHeight) 
    } 
} 

object Test { 
    val foo = new java.awt.geom.Ellipse2D.Double with RectangleLike 
} 

通過說self: java.awt.geom.RectangularShape =>聲明自我型的特質,使您能夠訪問諸如必要的getter和setter所有相應的方法,允許使用你的特質與所有RectangularShape的後代,並且還「限制」你的特質,因此它只能用作混合類,它們本身就是RectangularShape的子類。

替代到上述場景正在使用您RectangularShape的所謂視圖這是一種常見的範例爲好。爲此,你可以聲明一個類

class RichRectangularShape(shape: java.awt.geom.RectangularShape) { 
    def translate(dx: Double, dy: Double) { 
    shape.setFrame(shape.getX + dx, shape.getY + dy, 
        shape.getX + shape.getWidth, shape.getY + shape.getHeight) 
    } 
} 

Scala有的隱式觀察一個類型的對象作爲另一種類型的對象的方法。如果你碰巧在一個沒有用相應類型聲明的對象上調用一個方法,編譯器會試圖找到一個提供這種方法的類型,特別是還要尋找一個隱式轉換,以便你的原始對象可以被看作是一個後一種類型的實例。對於這個工作,你通常會申報RichRectangularShape同伴對象是這樣的:

object RichRectangularShape { 
    implicit def mkRRS(shape: java.awt.geom.RectangularShape): RichRectangularShape = 
    new RichRectangularShape(shape) 
} 

然後:

scala> import RichRectangularShape._ 
import RichRectangularShape._ 

scala> val foo = new java.awt.geom.Ellipse2D.Double 
foo: java.awt.geom.Ellipse2D.Double = [email protected] 

scala> foo.translate(5,2) 

scala> foo.getX 
res1: Double = 5.0 

scala> foo.getY 
res2: Double = 2.0 

scala> :t foo 
java.awt.geom.Ellipse2D.Double 
+1

@forNelton令人印象深刻!非常感謝,我真的被困在此。我必須研究隱含的觀點。 – ideathbird 2012-02-07 07:07:35

+0

不客氣。根據您的應用程序的類型,我認爲第一種方法可能更合適,因爲方法調用可能更快,而沒有任何隱含的麻煩。 – fotNelton 2012-02-07 07:58:42

0

確實令人印象深刻的解釋和例子!

我對這種方法有一個小問題,translate方法包含我想避免的實際計算。是的,這種情況很簡單,但一般來說這種方法可能會很複雜,導致嚴重的發展。因此,我建議以下方法:

trait RectangleLike extends java.awt.geom.RectangularShape { 
    def translate(dx: Int, dy: Int): Unit = { 
     val rect = new java.awt.Rectangle 
     rect.setRect(getX, getY, getWidth, getHeight) 
     rect.translate(dx,dy) 
     setFrame(rect.getX, rect.getY, rect.getWidth, rect.getHeight) 
     } 
} 

這樣我使用的是原來的translate計算。