7

I'd like to implement a java method that uses generics in scala (2.9.2). But I'm failing...

Java interface method:

public <T extends Number> void setAttribute(Key<T> key, Number value);

Scala code that want to implement that method:

def setAttribute[T <: Number](key: Key[T], value: Number) = {
  setAttributeLocal(key, value)  }

private def setAttributeLocal[T](key: Key[T], value: T) = {
  val stringValue = ConvertUtils.convert(value, classOf[String]).asInstanceOf[String]
  session = session + (key.getValue() -> stringValue)
}

Key looks like:

public class Key<T>

But this doesn't compile.

[error]  found   : mypackage.Key[T]
[error]  required: mypackage.Key[java.lang.Number]
[error] Note: T <: java.lang.Number, but Java-defined class Key is invariant in type T.
[error] You may wish to investigate a wildcard type such as `_ <: java.lang.Number`. (SLS 3.2.10)
[error]     setAttributeLocal(key, value)

I can't figure out what's the problem. Any suggestions/idea?

greez GarfieldKlon

6
  • 1
    We'll need to know how Key looks like. Because if I use public interface Key<T> the code above compiles fine. Commented Jul 30, 2012 at 14:24
  • @GarfieldKlon Which version of scala are you working with? Commented Jul 30, 2012 at 14:34
  • @0__ under which version does it compile? Commented Jul 30, 2012 at 14:35
  • @Richard Scala 2.9.1, 2.9.2, 2.10.0-M6 - indeed since I have no clue what setAttributeLocal would be, I did not try to write that. So the error must be there, hence the question needs to include that information. Commented Jul 30, 2012 at 14:45
  • 1
    What is the type signature of setAttributeLocal? Commented Jul 30, 2012 at 17:30

3 Answers 3

4

It appears the compiler is unhappy with your call to setAttributeLocal. setAttributeLocal requires a Key[Number], but you are providing a Key[_ <: T]. In Java-Land this means you're trying to pass a Key<? extends Number> off as a Key<Number>.

The suggestion is to have setAttributeLocal accept Key<? extends Number> or Key[_ <: Number], depending on whether it is Java- or Scala-defined.

Sign up to request clarification or add additional context in comments.

3 Comments

@GarfieldKlon I had to read this a few times... I think what Ben is suggesting is that if you're focused on fixing setAttribute then you may be looking at the wrong thing. The error you described is a type problem with setLocalAttribute, not setAttribute. What does your definition of setLocalAttribute look like?
@Richard - alright, that explains why I couldn't reproduce this
We finally added this solution: private def setAttributeLocal[T](key: Key[T], value: Object)
1

Something looks a bit off here.

Have you tried:

def setAttribute[T <: Number](key: Key[T], value: T) =
  setAttributeLocal(key, value)

It seems strange/bad to preserve the type T for the key, but not use it on the value. My guess is that's where you're getting an invariant error. You're trying to assing the value of type Number to a key of type T and the compiler isn't sure if it can't pass Number for T (while it knows it can pass T for Number).

Can we see more code?

2 Comments

That's not an option, because it isn't an override.
There are limitations in the type interfaces of Java and Scala. What you're doing in Java is type unsound, and so Scala disallows it. If you want to continue to use that type-wizardry, then you'll have to stick with Java's type system, sorry.
1

As @jsuereth already pointed out, there is a discrepancy between the signatures of setAttribute and setAttributeLocal, namely, that the former accepts a Key[T <: Number] but fixes the value that goes with the key to exactly be a Number, whereas the latter is more flexible and allows key and value to be T <: Number. This looks rather odd and you might want to reconsider that decision. It also leads to the problem explained by @Ben Schulz.

In any case, the compiler (2.9.1) is happy with the following setup:

MyI.java:

public interface MyI {
  public <T extends Number> void setAttribute(Key<T> key, Number value);
}

Key.java:

public interface Key<T> {
}

Test.scala:

object Test extends App {
  def setAttribute[T <: Number](key: Key[T], value: Number) = {
    setAttributeLocal(key, value)  }

  private def setAttributeLocal[T](key: Key[T], value: Number) = {
    /* Notice that value is now also of type Number. */
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.