This page is no longer maintained — Please continue to the home page at www.scala-lang.org

[JavaFX] Type hierarchy wondering...

6 replies
Philippe Lhoste
Joined: 2010-09-02,
User offline. Last seen 42 years 45 weeks ago.

I have another question...

I play a bit with JavaFX (beta 34) and it seems that something like:
new KeyValue(circle.translateXProperty, val)
used to work (for somebody else), but is now rejected by the compiler (because of Scala or
of JavaFX changes). It also work in the Java sample.

The first parameter of KeyValue must be a WritableValue, translateXProperty is a
DoubleProperty which implements the interface WritableValue

http://download.oracle.com/javafx/2.0/api/javafx/beans/property/DoublePr...
java.lang.Object
extended by javafx.beans.value.ObservableValueBase
extended by javafx.beans.binding.NumberExpressionBase
extended by javafx.beans.binding.DoubleExpression
extended by javafx.beans.property.ReadOnlyDoubleProperty
extended by javafx.beans.property.DoublePropertyBase
extended by javafx.beans.property.DoubleProperty
All Implemented Interfaces:
[...], WritableValue

http://download.oracle.com/javafx/2.0/api/javafx/beans/value/WritableVal...
javafx.beans.value
Interface WritableValue

I get the error:

error: type mismatch;
found : javafx.beans.property.DoubleProperty
required: javafx.beans.value.WritableValue[Any]
Note: java.lang.Number <: Any (and javafx.beans.property.DoubleProperty <:
javafx.beans.property.Property[java.lang.Number]), but Java-defined trait WritableValue is
invariant in type T.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)

I don't get the "invariant in type T" information, nor how to use the wildcard type. And
not why a class implementing an interface isn't usable as a parameter of this type.

I tried to do an implicit conversion:

implicit def dblProp2WritVal(dp: DoubleProperty): WritableValue[Double] =
dp.asInstanceOf[WritableValue[Double]]

(also tried with WritableDoubleValue)
but it looks ignored.

So I had to do something like:
new KeyValue(circle.translateXProperty.asInstanceOf[WritableValue[Double]], val)
but it is ugly... :-(

I am missing something?

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: Type hierarchy wondering...

Is this not working anymore?

new KeyValue[Number](circle.translateXProperty, val)

Philippe Lhoste
Joined: 2010-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Type hierarchy wondering...

On 12/07/2011 21:38, Dave wrote:
> Is this not working anymore?
>
> new KeyValue[Number](circle.translateXProperty, val)

No, using that or [Double] results in the error:

error: javafx.animation.KeyValue does not take type parameters

despite the information given by the JavaDoc of JavaFX 2...
Looks like some Scala-Java interoperability problem. I forgot to indicate I am using Scala
2.9, in case it is relevant.

Since I don't have much answers, I will try and do a micro-project to (hopefully)
reproduce the issue in a standalone setting (no dependencies).

BTW, thanks for having shared your code on ColorfulCircles, you can find my version, based
on your, at http://bazaar.launchpad.net/~philho/+junk/Scala/files/JavaFX/demos
(I think, LP is on maintenance mode right now, base address is
http://bazaar.launchpad.net/~philho/+junk/Scala/files at least)

I tried to stick closely at the design of the original one, while trying to use idiomatic
Scala as a learning process (ie. sometime overdoing it... :-)).

Philippe Lhoste
Joined: 2010-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Type hierarchy wondering...

Real URL of my remix of your version of ColorfulCircles in Scala is:
http://bazaar.launchpad.net/~philho/+junk/Scala/view/head:/JavaFX/demos/...

BTW, Stephen Chin made a presentation on using JavaFX with Scala, showing some DSL goodies
(like KeyValue(v -> t)) but nobody released such wrapper yet?
Currently I will probably stick with ad hoc conversion as I am not at a level to make my
own DSL (I already struggle to make a working implict, not to mention using Manifests or
such...), but having something ready out of the box (and on which we can build to have
consistent code across projects) would be nice.

Philippe Lhoste
Joined: 2010-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: [JavaFX] Type hierarchy wondering...

I can reproduce the bug on a simpler case...

I made a package v with an interface WritableValue:

package v;

public interface WritableValue
{
String V = "WritableValue";
}

I made a package p with a class DoubleProperty:

package p;

import v.WritableValue;

public class DoubleProperty implements WritableValue
{
final public String P = "DoubleProperty";

public DoubleProperty()
{
System.out.println(P);
}
}

I made a package a with a class KeyValue:

package a;

import p.DoubleProperty;
import v.WritableValue;

public final class KeyValue
{
public KeyValue(WritableValue target, T endValue)
{
System.out.println("KeyValue: " + target + " " + endValue);
}
}

then I made a Scala class to test this:

import _root_.a._
import _root_.p._
import _root_.v._

object Test
{
val dp = new DoubleProperty()
val x = new KeyValue[Double](dp, 25.0)
}

If I omit the [Double], I get the error I reported:

error: type mismatch;
found : p.DoubleProperty
required: v.WritableValue[Any]
Note: java.lang.Double <: Any (and p.DoubleProperty <: v.WritableValue[java.lang.Double]),
but Java-defined trait WritableValue is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)

OK, so we need a type, I put the [Double]. I get the error:

error: type mismatch;
found : p.DoubleProperty
required: v.WritableValue[Double]

which I don't understand, as DoubleProperty implements WritableValue[Double], and this
code works in Java:

import a.KeyValue;
import p.DoubleProperty;
import v.WritableValue;

public class Test
{
DoubleProperty dp = new DoubleProperty();
KeyValue x = new KeyValue(dp, 25.0);
}

Now, I decompiled KeyValue and saw why the given example actually uses something like:
KeyValue x = new KeyValue(dp, 25.0);
(probably to simplify the life of Java developers...)
It is defined instead as:

public final class KeyValue
{
public KeyValue(WritableValue target, T endValue) { ... }
}

ie. the class is untyped (hence the protestation from the compiler when I write
KeyValue[Double] on the Scala side!), but they typed the constructors and make a
convoluted instanceof dance to store the type in a member! (which explains why this class
is final...).

I join the little project I made to demonstrate the issue, it should be small enough to
avoid being an annoyance for the mailing list users...

Given that the Java class compiles fine, I would say it is a bug in the Scala compiler
(not able to detect the WritableValue/DoubleProperty relationship), so if somebody agree,
I can open a bug report.

Note that the actual DoubleProperty doesn't implement directly WritableValue, but inherit
it via the long class hierarchy shown in my previous message.

Fortunately, there is a workaround (cast) but it is quote ugly, and might spread in lot of
Scala/JavaFX projects (unless somebody shows me how to make a working implicit!).

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: Type hierarchy wondering...

I am afraid there is only your solution (runtime casting) if you don't
want to use a companion object. Alternative is with companion objects
(because I found out that I had almost no modification to get my
version with companion objects and convenience trait working).

colorfulcirclesb34v1.scala (using only 1 companion object for KeyValue
to fix conversion problem)
======================================================================

package colorfulcirclesb34v1

import javafx.animation.{KeyFrame, Timeline}
import javafx.application.Application
import javafx.scene.{Group, Node, Scene}
import javafx.scene.effect.{BlendMode, BoxBlur}
import javafx.scene.paint.{Color, CycleMethod, LinearGradient, Stop}
import javafx.scene.shape.{Circle, Rectangle, StrokeType}
import javafx.stage.Stage
import javafx.util.Duration
import javafx.beans.value.WritableValue
import scala.collection.JavaConversions._

object Main extends App {
Application.launch(classOf[App], args)
class App extends Application {
override def start(stage: Stage) {
val root = new Group
val scene = new Scene(root, 800, 600, Color.BLACK)
stage.setScene(scene)

val circles = new Group
for (i <- 0 until 30) {
val circle = new Circle(150, Color.web("white", 0.05))
circle.setStrokeType(StrokeType.OUTSIDE)
circle.setStroke(Color.web("white", 0.16))
circle.setStrokeWidth(4)
circles.getChildren.add(circle)
}
val colors = new Rectangle(scene.getWidth,
scene.getHeight,
new LinearGradient(0f, 1f, 1f, 0f,
true,

CycleMethod.NO_CYCLE,
new
Stop(0, Color.web("#c3c3c3")),
new
Stop(0.14, Color.web("#d8d8d8")),
new
Stop(0.28, Color.web("#c5c5c5")),
new
Stop(0.43, Color.web("#ababab")),
new
Stop(0.57, Color.web("#7f7f7f")),
new
Stop(0.71, Color.web("#949494")),
new
Stop(0.85, Color.web("#7f7f7f")),
new
Stop(1, Color.web("#868686"))
)
)
val blendModeGroup = new Group(new Group(new
Rectangle(scene.getWidth,

scene.getHeight,

Color.BLACK),
circles),
colors)
blendModeGroup.setBlendMode(BlendMode.OVERLAY)
root.getChildren.add(blendModeGroup)
circles.setEffect(new BoxBlur(10, 10, 3))

val timeline = new Timeline
circles.getChildren.view.zipWithIndex.foreach {
case (circle, index) => {
timeline.getKeyFrames.addAll(
new KeyFrame(Duration.ZERO, // set start
position at 0
KeyValue[Number]
(circle.translateXProperty, math.random * 800),
KeyValue[Number]
(circle.translateYProperty, math.random * 600)
),
new KeyFrame(new Duration(40000), // set
end position at 40s
KeyValue[Number]
(circle.translateXProperty, math.random * 800),
KeyValue[Number]
(circle.translateYProperty, math.random * 600)
)
)
}
}
// play 40s of animation
timeline.play

stage.setVisible(true)
}
}
}

object KeyValue {
import javafx.animation.KeyValue
//KeyValue(WritableValue target, T endValue)
def apply[T](target: WritableValue[T], endValue: T) = new
KeyValue(target, endValue)
}

colorfulcirclesb34v2.scala (all companion objects and convenience
trait and getChildren, foreach hidden away)
===============================================================================

package colorfulcirclesb34v2

import javafx.application.Application
import javafx.stage.Stage
import javafx.animation.{KeyFrame, KeyValue, Timeline}
import javafx.scene.{Group, Node, Scene, Parent}
import javafx.scene.effect.{BlendMode, BoxBlur}
import javafx.scene.paint.{Color, CycleMethod, LinearGradient, Stop,
Paint}
import javafx.scene.shape.{Circle, Rectangle, StrokeType}
import javafx.util.Duration
import javafx.beans.value.WritableValue
import javafx.collections._
import scala.collection.JavaConversions._

object Main extends ScalaFX {
Application.launch(classOf[App], args)
class App extends Application {
override def start(stage: Stage) {
val root = Group()
val scene = Scene(root, 800, 600, Color.BLACK)
stage.setScene(scene)

val circles = Group()
for (i <- 0 until 30) {
val circle = Circle(150, Color.web("white", 0.05))
circle.setStrokeType(StrokeType.OUTSIDE)
circle.setStroke(Color.web("white", 0.16))
circle.setStrokeWidth(4)
circles.add(circle)
}
val colors = Rectangle(scene.getWidth,
scene.getHeight,
LinearGradient(0f, 1f, 1f, 0f,
true,

CycleMethod.NO_CYCLE,
Stop(0,
Color.web("#c3c3c3")),
Stop(0.14,
Color.web("#d8d8d8")),
Stop(0.28,
Color.web("#c5c5c5")),
Stop(0.43,
Color.web("#ababab")),
Stop(0.57,
Color.web("#7f7f7f")),
Stop(0.71,
Color.web("#949494")),
Stop(0.85,
Color.web("#7f7f7f")),
Stop(1,
Color.web("#868686"))
)
)
val blendModeGroup = Group(Group(Rectangle(scene.getWidth,

scene.getHeight,
Color.BLACK),
circles),
colors)
blendModeGroup.setBlendMode(BlendMode.OVERLAY)
root.add(blendModeGroup)
circles.setEffect(BoxBlur(10, 10, 3))

val timeline = Timeline()
for (circle <- circles) {
timeline.addAll(
KeyFrame(Duration.ZERO, // set start position
at 0
KeyValue[Number]
(circle.translateXProperty, math.random * 800),
KeyValue[Number]
(circle.translateYProperty, math.random * 600)
),
KeyFrame(Duration(40000), // set end position
at 40s
KeyValue[Number]
(circle.translateXProperty, math.random * 800),
KeyValue[Number]
(circle.translateYProperty, math.random * 600)
)
)
}
// play 40s of animation
timeline.play

stage.setVisible(true)
}
}
}

trait ScalaFX extends App {

// Companion objects for constructors for removing new keyword
// Implicit conversions for removing getChildren, getKeyframes
object Group {
//Group()
def apply() = new Group
//Group(Node... children)
def apply(children: Node*) = { new Group(children : _*)}
}
//Group implicit conversions
implicit def getChildren(g: Group) : ObservableList[Node] =
g.getChildren()
implicit def foreach(g: Group) = for(n <- g.getChildren()) yield n
object Scene {
//Scene(Parent root, double width, double height, Paint fill)
def apply(root: Parent, width: Double, height: Double,
fill:Paint) = new Scene(root, width, height, fill)
}
object Circle {
//Circle(double radius, Paint fill)
def apply(radius: Double, fill: Paint) = new Circle(radius,
fill)
}
object Rectangle {
//Rectangle(double width, double height, Paint fill)
def apply(width: Double, height: Double, fill: Paint) = new
Rectangle(width, height, fill)
}
object LinearGradient {
//LinearGradient(double startX, double startY, double endX,
double endY, boolean proportional, CycleMethod cycleMethod, Stop...
stops)
def apply(startX: Double, startY: Double, endX: Double, endY:
Double, proportional: Boolean, cycleMethod: CycleMethod, stops: Stop*)
=
new LinearGradient(startX, startY, endX, endY,
proportional, cycleMethod, stops: _*)
}
object Stop {
//Stop(double offset, Color color)
def apply(offset: Double, color: Color) = new Stop(offset,
color)
}
object BoxBlur {
//BoxBlur(double width, double height, int iterations)
def apply(width: Double, height: Double, iterations: Int) =
new BoxBlur(width, height, iterations)
}
object Timeline {
//Timeline()
def apply() = new Timeline
}
//Timeline implicit conversions
implicit def getKeyFrames(t: Timeline) : ObservableList[KeyFrame]
= t.getKeyFrames()
object KeyFrame {
//KeyFrame(Duration time, KeyValue... values)
def apply(time: Duration, values: KeyValue*) = new
KeyFrame(time, values : _*)
}
object KeyValue {
//KeyValue(WritableValue target, T endValue)
def apply[T](target: WritableValue[T], endValue: T) = new
KeyValue(target, endValue)
}
object Duration {
//Duration(double millis)
def apply(millis: Double) = new Duration(millis)
//static Duration ZERO
val ZERO = javafx.util.Duration.ZERO
}
}

Philippe Lhoste
Joined: 2010-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Type hierarchy wondering...

On 13/07/2011 14:36, Philippe Lhoste wrote:
> Given that the Java class compiles fine, I would say it is a bug in the
> Scala compiler (not able to detect the WritableValue/DoubleProperty
> relationship), so if somebody agree, I can open a bug report.

It isn't a blocking issue, but I am still curious whether I just missed
something in the Scala type handling (more than likely...) or if it is a
real bug worth reporting...

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland