2012-02-07 61 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计算。