My goal is to enhance inside scala code an existing Java class using a trait mix-in. For example to add a method like java.awt.Rectangle.translate(dx, dy) to java.awt.geom.Ellipse2D class. For this I create the following 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
Then use the trait when constructing an Ellipse:
val egg = new java.awt.geom.Ellipse2D.Double(5, 10, 20, 30) with RectangleLike
However when I execute the above script in scala REPL I get the following output:
<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
I suspect that this error is due to the way Scala implements vars - as a private field and a getter/setter pair of methods. Is what I try to achieve doable? Is there another way to define the java class fields in the trait and then refer to them inside the concrete trait methods?
Thanks in advance Jack Dimas
Yes, it is doable but instead of trying to access the private fields of the classes you want to mix in with (which is most likely a bad idea anyway), you would want to declare the self-type of RectangleLike
to be java.awt.geom.RectangularShape
so that you can use your trait with e.g. Ellipse2D.Double
just as well as with Rectangle2D.Double
.
Here is how it works:
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
}
By saying self: java.awt.geom.RectangularShape =>
you declare the self-type of your trait which enables you to access all corresponding methods like the necessary getters and setters, allows for using your trait with all descendants of RectangularShape
, and also "restricts" your trait so that it can only be used as a mixin to classes which themselves are subtypes of RectangularShape
.
The alternative to the above scenario is using a so-called view of your RectangularShape
which is a common paradigm as well. For this, you would e.g. declare a class
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 has a way of implicitly viewing an object of one type as an object of another type. If you happen to call a method on a object which is not declared in its corresponding type, the compiler trys to find a type that provides this method and in particular also trys to find an implicit conversion so that your original object can be viewed as an instance of the latter type. For this to work, you would typically declare the companion object of RichRectangularShape
as something like this:
object RichRectangularShape {
implicit def mkRRS(shape: java.awt.geom.RectangularShape): RichRectangularShape =
new RichRectangularShape(shape)
}
Then:
scala> import RichRectangularShape._
import RichRectangularShape._
scala> val foo = new java.awt.geom.Ellipse2D.Double
foo: java.awt.geom.Ellipse2D.Double = java.awt.geom.Ellipse2D$Double@0
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With