我的目标是使用特征混合来增强现有Java类的内部scala代码。例如,将java.awt.Rectangle.translate(dx,dy)等方法添加到java.awt.geom.Ellipse2D类中。为此,我创建了以下特征:
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
但是当我在scala 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
我怀疑这个错误是由于Scala实现变量的方式 - 作为私有字段和getter / setter方法对。我努力实现的目标是什么?是否有另一种方法可以在特征中定义java类字段,然后在具体的特征方法中引用它们?
提前致谢 杰克迪马斯
答案 0 :(得分:8)
是的,它是可行的,但不是试图访问你想要混合的类的私有字段(这很可能是一个坏主意),你会想要声明{{1的自我类型成为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
}
你声明你的特质的自我类型,它允许你访问所有相应的方法,如必要的getter和setter,允许你使用你的特征self: java.awt.geom.RectangularShape =>
的所有后代,和也“限制”你的特性,使它只能作为一个混合使用,它们本身是RectangularShape
的子类型。
上述方案的替代使用了RectangularShape
的所谓视图,这也是一种常见的范例。为此,您可以例如声明一个类
RectangularShape
Scala有一种隐式查看一种类型的对象作为另一种类型的对象的方法。如果您碰巧在未在相应类型中声明的对象上调用方法,则编译器会尝试查找提供此方法的类型,特别是还可以尝试查找隐式转换,以便可以将原始对象视为后一种类型的实例。为此,您通常会将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)
}
}
的伴随对象声明为:
RichRectangularShape
然后:
object RichRectangularShape {
implicit def mkRRS(shape: java.awt.geom.RectangularShape): RichRectangularShape =
new RichRectangularShape(shape)
}
答案 1 :(得分: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
计算。