Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala compilation error with Java parametrized constructor and parametrized interface

When trying to compile the following code with Scala 2.8.1/JavaFx 2.0 beta

new KeyValue(circle.translateYProperty, random() * height)

I get the following error:

[error]  found   : javafx.beans.property.DoubleProperty
[error]  required: javafx.beans.value.WritableValue[Any]
[error]             new KeyValue(circle.translateYProperty, random() * height)
[error]                                 ^
[error] one error found

Whereas this line gets compiled just fine:

new KeyValue(circle.translateXProperty.asInstanceOf[WritableValue[Any]], random() * width)

I checked the KeyValue constructor and it has the following signature:

public <T> KeyValue(javafx.beans.value.WritableValue<T> tWritableValue, T t) { /* compiled code */ }

circle.translateXProperty returns DoubleProperty which implements the following interface:

public interface WritableNumberValue extends javafx.beans.value.WritableValue<java.lang.Number>

What would be more elegant solution than casting to make it compile?

like image 984
Stas Avatar asked Jan 19 '23 01:01

Stas


2 Answers

-- Revised answer, based on Exception and Blaisorblade's comments --

You've hit a limitation of Scala's application of implicits, rather than (just) a Scala-Java interop problem. Here's a simplified example,

class Foo[T]
def f[T](x: Foo[T], y: T): T = y

f(new Foo[Number], new java.lang.Double(0)) // OK; infers T==Number
f[Number](new Foo[Number], 0)               // OK; uses implicit int2Integer(0)
// f(new Foo[Number], 0)                    // error
  • The first call to f works because the common supertype of java.lang.Double and java.lang.Number is java.lang.Number, so that is the type inferred for T.

  • The second call to f works because we've explicitly told the compiler that T==java.lang.Number. When the compiler finds the second argument, 0 : Int, doesn't match the expected type java.lang.Number, it searches for an implicit conversion from Int to Number. The compiler finds Predef.int2Integer and applies it. All is well.

  • The third call to f doesn't work, because the first parameter constrains T == Number, and the second argument says T >: Int (that is, T is a supertype of Int). The common supertype of Int and Number is Any, but that won't work because Foo[T] is not covariant in T (in other words, we can't cast a Foo[Number] to a Foo[Any]). That's the gist of the compiler's error message. Note that the compiler doesn't know how to apply an implicit conversion, because it doesn't know a specific type of T to convert to.

One strange thing about the JavaFX code that you posted is that the KeyValue class is not generic, but is has a generic constructor. Interestingly, this is not possible in Scala, so there's no way (as far as I can tell), to explicitly constrain the parameter T from Scala code. If the entire KeyValue class were generic, you'd also be able to write

new KeyValue[Number](circle.translateYProperty, random() * height)

which would be equivalent to the code that Exception posted, since the compiler would infer the double2Double conversion.

like image 90
Kipton Barros Avatar answered Jan 31 '23 20:01

Kipton Barros


I had the same problem a few days ago. After trying different things i ended up with that.

new KeyValue(circle.translateYProperty, double2Double(random() * height))

(see Blaisorblade comments for explanation)

like image 43
lucidd Avatar answered Jan 31 '23 18:01

lucidd