user2741287 user2741287 - 3 months ago 11
Scala Question

Java Wildcard Generic Type Interop From Scala

I'm writing in scala and I'm dealing with a Java API which returns a

List<? extends IResource>
, where
IResource
is a generic parent interface (the details, if it helps).

I'm trying to add an
IResource
to the list returned by that method, but I can't get my code to compile (
Patient
is a java class which implements
IResource
, and getContainedResources returns the
List<? extends IResource>
):

Here is my original code

val patient = new Patient()
patient.setId(ID)
val patientASResource: IResource = patient
entry.getResource.getContained.getContainedResources.add(patient)


And here is the error I get:

type mismatch;
found : patientASResource.type (with underlying type ca.uhn.fhir.model.api.IResource)
required: ?0 where type ?0 <: ca.uhn.fhir.model.api.IResource
entry.getResource.getContained.getContainedResources.add(patientASResource)
^
one error found


Notice that I'm trying to add
patientASResource
which I've typed-up to the interface
IResource
. Trying to add
patient
(the class implementing the interface) has a worse error message.

Other things I've tried:

//From what I understand of "Java wildcards" per here: http://stackoverflow.com/a/21805492/2741287
type Col = java.util.Collection[_ <: IResource]
val resList: Col = entry.getResource.getContained.getContainedResources
val lst: Col = asJavaCollection(List(patient))
resList.addAll(lst)


Doesn't work either, it returns something like:

type mismatch
found : java.util.Collection[_$1(in method transformUserBGs)] where type _$1(in method transformUserBGs) <: ca.uhn.fhir.model.api.IResource
required: java.util.Collection[_ <: _$1(in type Col)]
resList.addAll(lst)
^

Answer

The problem isn't with interop. This definitely shouldn't compile, and neither should the equivalent Java code.

List<? extends IResource> means it can be a List<IResource>, List<Patient>, List<SomeSubclassOfPatient>, List<SomeOtherResourceUnrelatedToPatient>, etc. and you don't know which. Thus, adding a Patient (or an IResource after upcasting) to such a list is not allowed.

If you somehow know that in your specific situation entry is such that entry.getResource.getContained.getContainedResources returns a List[IResource], or a List[Patient], you should attempt to ensure this statically by specifying this when overriding getContainedResources. If this is impossible, the last recourse is to cast:

entry.getResource.getContained.
  getContainedResources.asInstanceOf[java.util.List[IResource]].add(patient)

Just to reiterate: you should avoid this if at all possible.