I’ve found a problem with Scala 2.13.2 while reviewing a code. Here I bring the simple and small as possible piece of code with respective error messages. The errors occur in both using Scalac and Dotty in command line (scalac ComboBoxProblem.scala), no IDE. This code use to compile in previews versions of Scala. If the comments are removed there is only the second error. A compile error that I couldn’t solve. A solution would be very welcomed.
object AbstractInterface {}
abstract class AbstractInterface {}
@SerialVersionUID(1L)
trait ComboBoxChild[T] extends JComboBox[T] with Serializable {
val comboClass = new ComboBoxClass()
protected class ComboBoxClass extends BasicComboBoxUI {
override protected def createPopup(): ComboPopup = {
val popupChild = new ComboPopupChild(comboBox.asInstanceOf[JComboBox[AbstractInterface]])
//popupChild.getAccessibleContext.setAccessibleParent(comboBox)
//popupChild
}
}
@SerialVersionUID(1L)
protected class ComboPopupChild(comboChild: JComboBox[AbstractInterface]) extends BasicComboPopup( comboChild) {}
}
ComboBoxProblem.scala:26: error: type mismatch;
found : Unit
required: javax.swing.plaf.basic.ComboPopup
}
^
ComboBoxProblem.scala:30: error: type mismatch;
found : javax.swing.JComboBox[AbstractInterface]
required: javax.swing.JComboBox[Object]
Note: AbstractInterface <: Object, but Java-defined class JComboBox is invariant in type E.
You may wish to investigate a wildcard type such as _ <: Object. (SLS 3.2.10)
protected class ComboPopupChild(comboChild: JComboBox[AbstractInterface]) extends BasicComboPopup( comboChild) {}
^
2 errors
OK, people could run it also in Scastie with no problems. I’m using Java
openjdk version “11.0.8” 2020-07-14
OpenJDK Runtime Environment (build 11.0.8+10-suse-lp151.3.19.1-x8664)
OpenJDK 64-Bit Server VM (build 11.0.8+10-suse-lp151.3.19.1-x8664, mixed mode)
And Scala
Scala code runner version 2.13.2 – Copyright 2002-2020, LAMP/EPFL and Lightbend, Inc.
All in Linux OpenSuse 15.1. The errors are there. It seems a bug in some point of this environment. Either the bug is fixed or I need to find a work around, yet thanks for the answer because I’ll focus in environment rather than code that is correct.
Oh, I know what this is, it’s a Java 8 vs 11 thing. The type mismatch error is reproducible for me if I use Java 11. Oracle made source-incompatible changes to the Swing APIs. I don’t know the details.
Just for the record: this compilation issue is way much more serious knowing that the Scala swing code compiled in Java 8 will crash in Java 11, meaning you must keep an out of date JVM to run any Scala code.
Well, at least one side, either Scala or Java, or both, must fix this issue, otherwise any advance will always have to rely on an old technology.
The problem is simply that Scala Swing is “mostly unsupported”. Not enough manpower to maintain it adequately. Either use Java Swing directly (from Scala, but not using Scala Swing), or use something else (maybe ScalaFX?).
(But I haven’t dug into it enough to be certain. Maybe things would be different in Java because of use-site variance vs. declaration-site variance? The other relevant Java/Scala difference is that the Scala type system allows primitive types as type parameters, and Java doesn’t. As a result in Scala sometimes you may need an <: AnyRef or something like that.)
I’ve seen a program that works perfectly in Java 8 but crashes in Java 11, which is not my code, actually a large project, which I don’t think it is possible a small sample. It would be nice of course an example to check. For the time being just trust, and hopefullly all will be fixed in the near future.
The former using a raw type of the generic class JComboBox, the latter – properly – using a type argument for the generic class. This clashes now, since you tried to pass a JComboBox[AbstractInterface] instead of a JComboBox[AnyRef].
You can fix the code for both JVM targets:
@SerialVersionUID(1L)
protected class ComboPopupChild(comboChild: JComboBox[AbstractInterface])
extends BasicComboPopup(comboChild.asInstanceOf[JComboBox[AnyRef]]) {}
I could find and separate the piece of code that compiles and run in Java 8 but crashes in Java 11 (the compíled Java 8 ). It is just to complete the discussion. The code is:
RunTimeProblem.java
public class RunTimeProblem {
public static void main(String[] args) {
System.out.println("RunTimeProblem");
try {
final ClassLoader classLoader = new ChildClassLoader(new String("String"));
final Class childClass = Class.forName("ClassName", false, classLoader);
} catch (ClassNotFoundException ex) { }
}
}
ChildClassLoader.java
import java.net.URLClassLoader;
public final class ChildClassLoader extends ClassLoader {
RunTimeProblem
ChildClassLoader
Exception in thread “main” java.lang.ClassCastException: class jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to class java.net.URLClassLoader (jdk.internal.loader.ClassLoaders$AppClassLoader and java.net.URLClassLoader are in module java.base of loader ‘bootstrap’)
at ChildClassLoader.findClass(ChildClassLoader.java:18)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:398)
at RunTimeProblem.main(RunTimeProblem.java:15)
That’s an unsafe code by definition, i.e. unsafe cast. You don’t have guarantee that there’s a URLClassLoader. You have to test for multiple possibilities.