Since update 9-u175, java permits illegal access by default thus allowing all the old reflection tricks. Works fine, except when it comes to classes in control.skin (maybe others as well, didn't check) - to reproduce, run the example below, click the button and see how access succeeds until the line that tries to access a private field in ButtonSkin. The stacktrace:
Exception in thread "JavaFX Application Thread" java.lang.reflect.InaccessibleObjectException:
Unable to make field private final com.sun.javafx.scene.control.behavior.BehaviorBase javafx.scene.control.skin.ButtonSkin.behavior accessible:
module javafx.controls does not "opens javafx.scene.control.skin" to unnamed module @537fb2
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:281)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:176)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:170)
My context: jdk9-u175, eclipse-oxygen-R with patch for java9, access rules in the project are set to allow javafx/**
The question is: who's the culprit? FX, Eclipse, the ea or ..?
The example:
import java.lang.reflect.Field;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.SkinBase;
import javafx.scene.control.skin.ButtonSkin;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler;
public class AccessFieldFX extends Application {
private Parent getContent() {
Button button = new Button("something to click on");
// okay
Object def = invokeGetFieldValue(Button.class, button, "defaultButton");
button.setOnAction(e -> {
ButtonSkin skin = (ButtonSkin) button.getSkin();
// okay
LambdaMultiplePropertyChangeListenerHandler cl =
(LambdaMultiplePropertyChangeListenerHandler) invokeGetFieldValue(SkinBase.class, skin, "lambdaChangeListenerHandler");
// okay
Object clField = invokeGetFieldValue(LambdaMultiplePropertyChangeListenerHandler.class, cl, "EMPTY_CONSUMER");
// failure
Object beh = invokeGetFieldValue(ButtonSkin.class, skin, "behavior");
});
BorderPane pane = new BorderPane(button);
return pane;
}
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent(), 600, 400));
// primaryStage.setTitle(FXUtils.version());
primaryStage.show();
}
public static Object invokeGetFieldValue(Class declaringClass, Object target, String name) {
try {
Field field = declaringClass.getDeclaredField(name);
field.setAccessible(true);
return field.get(target);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(AccessFieldFX.class.getName());
}