I switched from Java 8 to Java 17. The code now doesn't compile
Specifically, imports from the com.sun.java.swing.plaf.windows
package are now invalid.
java: package com.sun.java.swing.plaf.windows does not exist
However, it does exist. It's just the new compiler being unreasonable. It now refuses to acknowledge its existence for some reason.
compilation error, com.sun.java.swing.plaf.windows does no exist
These com.sun.java.swing packages were never intended to be used outside of Java itself, and shouldn't have been used directly. Java now enforces that.
Alright, but here we are, we reference the package anyway. Java claims to be backwards-compatible, so anything that's legal in an older version should be legal in a newer one
So what do we do?
// MRE. Compilation fails. Amazon Corretto 17
package demos.button;
import com.sun.java.swing.plaf.windows.WindowsLookAndFeel;
import javax.swing.*;
import java.awt.*;
public class CheckBoxDemo {
public static void main(String[] args) throws UnsupportedLookAndFeelException, ClassNotFoundException,
InstantiationException, IllegalAccessException {
// you don't expect us to pass strings directly, do you?
UIManager.setLookAndFeel(WindowsLookAndFeel.class.getName());
JFrame frame = new JFrame("Check Box demo");
JPanel mainPanel = createMainPanel();
frame.setContentPane(mainPanel);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
private static JPanel createMainPanel() {
FlowLayout layout = new FlowLayout();
layout.setAlignment(FlowLayout.CENTER);
JPanel mainPanel = new JPanel(layout);
mainPanel.add(createCheckBox());
return mainPanel;
}
private static JCheckBox createCheckBox() {
JCheckBox checkBox = new JCheckBox();
return checkBox;
}
}
Even if I disable the --release
option, the code doesn't compile anyway. For example, because ComboPopup
's getList()
used to look like this
public JList getList();
and now it looks like this
public JList<Object> getList();
So if your ComboPopup
implementation used any type arguments, tough luck
// no longer compiles
@Override
public JList<T> getList() {
return itemList;
}
It doesn't look like backwards-compatibility at all. Isn't it (along with cross-platform support) the whole point of Java?
Since Java 9, Java is modular, and things you shouldn't be using are not exported from those modules, including public classes that are not part of the API defined by the Java specification. The classes in com.sun.*
in most cases are specific to the Sun/Oracle implementation of Java, and sometimes even platform-specific (as is the case for this look-and-feel), and not part of the Java specification.
As such, they are generally considered an implementation detail and not part of any API, and thus not subject to the backwards-compatibility promises of Java. If you'd used a Java implementation of a different vendor, you would likely not have access to them either (because they wouldn't exist).
So, since Java 9, when modules were implemented, you can no longer access these classes, even if they still exist (the package com.sun.java.swing.plaf.windows
is located in the java.desktop
module, at least in Windows builds). In theory you can still access them if you add the necessary exports (e.g. with --add-exports
on the commandline, or with Add-Exports
in them manifest of the application JAR).
However, it is far easier to replace your code with something which will simply work without exports, in a way that Sun and now Oracle have always recommended (e.g. see the Swing tutorial How to Set the Look and Feel which is based on Java 8).
You have multiple options:
Explicitly set the look and feel by class name:
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
I wouldn't recommend this, as it still ties you to Oracle/OpenJDK variants of Java.
Use the name of the look and feel instead of the class name (introduced in Java 9):
UIManager.setLookAndFeel(UIManager.createLookAndFeel("Windows");
Note that the Java 8 tutorial I linked mentions the names Windows XP
and Windows Vista
, but the names are actually Windows Classic
and Windows
. I can only assume that when this feature was introduced in Java 9, they considered the names mentioned in the Java 8 tutorial not right for this.
If the Java implementation or platform doesn't actually have the specified look and feel name, this will result in an UnsupportedLookAndFeelException
.
Make it dynamic based on the actual system:
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());