1

I have this below Class hierarchy in Java

Class Container 
{
    private List<? extends Element> element;
    public List<? extends Element> getElements()
    {
        return elements;
    }

    public void setElements(List<? extends Element> elements)
    {
        this.elements = elements;
    }
}

MyElement extends Element 
{
 int a;
}

In my scala services, I use these classes as below -

var elements: java.util.List[_ <: Element] = container getElements

elements match {
  case null => elements = new ArrayList[MyElement]();
  case _    => ;
}
//additional service code here, 
//and then try to create an object and add it to the existing list of elements as below. 

val element = new MyElement
//..... other prog logic..
elements.add(element)

I get a compilation error when the element is added to the list

I see that the error message is listed as -

type mismatch; found : element.type (with underlying type    com.vo.MyElement) required: _$7 where type _$7 <: com.vo.Element

Is there any way to fix this issue? Why is Scala compiler not able to decipher that MyElement is indeed a subclass of Element and can be added to the main list.

Really hope someone helps me out. I have tried several alternatives and I just can't get this working. Am I missing something or am I really stupid?

Thanks, Adarsh

  • this is neither Java nor Scala - `MyElement extends Element `. Similarly this - `Class Container `. What is `Element` ? what is `container` ? What is this - `case _ => ;` ? – sarveshseri Feb 09 '17 at 14:23
  • Can you please fix the question and explain what is that you are trying to do ? – sarveshseri Feb 09 '17 at 14:25
  • I think the statement that rises the compilation error is `elements = new ArrayList[MyElement]()`. Java `List` are invariant in generic type: how the hell can you assign an `ArrayList` to a `List`? Or am I missing something? – riccardo.cardin Feb 09 '17 at 14:27
  • 1
    @riccardo.cardin The question has too many problems... I see every line as having issues. – sarveshseri Feb 09 '17 at 14:29
  • @Adarsh Are your sure your Java code part is fine ? – sarveshseri Feb 09 '17 at 14:33
  • I mean, you can use it straight by copy-paste and it has several classes. Hence I mention it as class hierarchy. Of course, they are different classes and `container` is an instance of Container. –  Feb 09 '17 at 14:40

2 Answers2

2

If you tried this in Java you would have the same problem (only with a more helpful error message probably...).

Take a look at the following code and think about what would happen if this compiled.

trait Element 
class MyElement extends Element
class MyOtherElement extends Element

import java.{ util => ju }
val myElements = new ju.ArrayList[MyElement]()
val elements: ju.List[_ <: Element] = myElements
elements.add(new MyOtherElement)
val myElement: MyElement = myElements.get(0)

By the way, this has nothing to do with Java vs Scala:

import scala.collection.mutable.ListBuffer
val myElements = new ListBuffer[MyElement]()
val elements: ListBuffer[_ <: Element] = myElements
elements += new MyOtherElement
val myElement: MyElement = myElements(0)

The reason is that you are using use-site variance here, because mutable collections are defined as invariant. And all Java collections are automatically invariant because Java has no notion of declaration-site variance like Scala, and its collections are mutable anyway.

Community
  • 1
  • 1
Jasper-M
  • 14,966
  • 2
  • 26
  • 37
  • Actually, I have this working in java, the only change which should be done here as well is val myElements = new ju.ArrayList[Element](). Even on doing this change, I cannot add an element of Subclass to the arraylist of Superclass.... –  Feb 09 '17 at 14:34
  • Yes but the point is that everything I wrote compiles, except for the `elements.add(new MyOtherElement)` add part, because that would cause everything to blow up at runtime. – Jasper-M Feb 09 '17 at 14:37
  • It works when these classes are a part of Scala Hierarchy. But I have them in Java classes & this should absolutely have no issues. But unfortunately, I get this error. –  Feb 09 '17 at 14:38
  • The compiler cannot know that the actual type of the instance in `elements` is a `ju.ArrayList[Element]` or a `ju.LinkedList[MyElement]`, all it knows is that it's something that conforms to `ju.List[_ <: Element]`. And no it has nothing to do with Java classes vs Scala classes. See my edit. – Jasper-M Feb 09 '17 at 14:42
  • I understand that it is not Java vs Scala - but the point was/is that the `container.getElements()` returns `List extends NetworkElement>`. So, I think this is where the issue occurs. –  Feb 09 '17 at 14:52
  • Brilliant, Thank you for the last edit. It was great to read the link and understand the perspective. That was the problem! –  Feb 09 '17 at 14:56
0

Although, I am not sure about your Java code part but as far as I have understood, you are trying to do the following,

  1. you have some instance of Constainer as container.
  2. you want to get that container's elements and add a new element to it.

If this is what you are trying to do, then you can do the following,

val elements = {
  if (container.getElements() != null) {
    container.getElements()
  }
  else {
    val list: java.util.List[_ <: Element] = new java.util.ArrayList[MyElement]()
    list
  }
}


val element = new MyElement

elements.add(element)
sarveshseri
  • 13,738
  • 28
  • 47
  • This does not solve the problem for me. Because the java library class has a getter that returns `List extends Element> elements`. Needed to change that to `List super Element> elements` –  Feb 09 '17 at 14:57