Hi Jeffrey,

See my answer inline...

On 01/12/18 19:26, jeffrey kutcher wrote:
This is a good discussion but none of the suggestions satisfy the need I'm looking for.https://stackoverflow.com/questions/450807/how-do-i-make-the-method-return-type-generic

The example code below shows button0 being added to a VBox. It also shows 
button1 being added to the same VBox only instead of being added directly to 
the list returned by vbox.getChildren(), that is 
vbox.getChildren().add(button1), buttton1 is added via 
reflection/introspection. The end goal is the same in both cases. Add a button. 
The methodology is different. Regardless, this should be trivial. It's not. Why?
In this example, adding button0 produces no compile time warnings, Attempt-1. 
As for the code adding button1, Attempt-3 too produces no compile time warning, 
yet when run produces the WARNINGS shown at the bottom of the code. Commenting 
out Attempt-3 and uncommenting Attempt-2, produces compile time warnings.
The bottom of the code example shows how to compile and run the example.
As is, this code references no illegal reference code and yet still produces 
run time WARNINGS.
Is there a way to access the list returned by getChildren() via reflection so a 
widget can be added without producing the run time WARNINGS? It's not the 
WARNINGS that I'm concerned about. I'm concerned that when future Java releases 
become available this code will fail to work. I would like to find a solution 
so that doesn't happen and this code continues to work.
Maps have similar behavior. Try and use reflection to iterate over a Map, a 
trivial exercise, and it quickly becomes an impossible task. The internal class 
of the Map isn't public making it impossible to iterate through the list. I'm 
assuming that is what we are seeing here as well.
Personally I believe internal classes should never be used. They seem to 
produce more problems than they solve. They are a quick, lazy fix that usually 
results in painful long term support. Besides, internal classes defeat write 
once, run anywhere. If it's internal, it's not being used anywhere else; which 
means the code has no useful value elsewhere; obviously false. That implies the 
code will be duplicated elsewhere to leverage it's functionality, meaning the 
code is now written twice, defeating the montra. Therefore, internal classes 
should never be used. In the same spirit, classes should never be private for 
the same reason. The nice thing is, convention easily satisfies this 
suggestion. Just because you can, doesn't mean you should.
Java has been out for 23 years. Does a solution exist? I would like to see this 
example code work for future releases of Java.
Is there a solution? Would you please point me at an example.
Thanks
---------- BEGIN SOURCE ----------

public class ReflectAccessBug extends javafx.application.Application {

     public static void main(String[] args) {
         launch(args);
     }

     public void start(javafx.stage.Stage stage) {
         javafx.scene.control.Button button0 = new 
javafx.scene.control.Button();
         javafx.scene.control.Button button1 = new 
javafx.scene.control.Button();
         javafx.scene.layout.VBox vbox = new javafx.scene.layout.VBox();

         javafx.event.EventHandler<javafx.event.ActionEvent> event = e -> {
             javafx.scene.control.Button b =
                 (javafx.scene.control.Button)e.getSource();
             System.err.println(b.getText() + " " + e);
         };

         button0.setText("button0");
         button0.setOnAction(event);

         button1.setText("button1");
         button1.setOnAction(event);

         vbox.getChildren().add(button0);

         // Attempt-1
         // This works as expected.
         //vbox.getChildren().add(button1);

         try {
             // Now let's try adding button1 using introspection ...
             java.lang.reflect.Method m =
                 vbox.getClass().getMethod("getChildren", new Class[0]);
             Object o = m.invoke(vbox, new Object[0]);
/*
             // Attempt-2
             // This causes the same runtime warning as Attempt-3
             // and I don't believe it should.
             // Attempt-2 produces a compile time warning.
             // Attempt-3 eliminates the compile time warning.
             javafx.collections.ObservableList<javafx.scene.Node> l =
                 (javafx.collections.ObservableList<javafx.scene.Node>)o;
             m = l.getClass().getMethod("add", new Class[] { Object.class, });

Try this instead of above line:

m = javafx.collections.ObservableList.class.getMethod("add", new Class[] { Object.class, });
m.invoke(o, ....);

The problem is that when you say: o.getClass().getMethod(....), you start searching for a method in the runtime implementation class of the object 'o' (it doesn't matter if you assign variable 'o' to 'l' with a cast - 'l' will point to the same runtime object as 'o'). The method might be found in some class/interface that is not public or not exported. Class#getMethod always returns the Method object describing the most specific method - the method declared in the most specific type (see the javadoc of getMethod() for more accurate description).  What you may get back from such call is a Method object describing the declaration of the method in the implementation class. Although such method is public, the declaring class (implementation class) might not be accessible (either not public or not exported).

You better start searching for a method in some well known public type (ObservableList interface for example) that you know declares the method that is implemented or overridden in the runtime implementation class. You will get a different Method object, describing the abstract interface method, but when you invoke the method via this Method object, the invocation will be dispatched to the most specific implementation of that method because it obeys the virtual dispatch semantics. Reflection access checks are unfortunately performed with the declaring type/method described in the Method object and not with the method that the invocation actually dispatches to.

Hope this helps.

Regards, Peter

             o = m.invoke(l, new Object[] { button1, });
System.err.println("o="+o);
*/
             // Attempt-3
             // This results with the same runtime warning as Attempt-2.
             // I don't believe it should produce this warning and is the
             // point of this exercise.
             // Attempt-3 produces *NO* compile time warning as in Attempt-2.
             // It's not the compile time warning I'm concerned about. It's the
             // runtime warning that is the issue.
             // This code references no illegal reference code and yet still
             // produces a runtime warning. It references
             // "illegal reflective access operations". It's not this code
             // that is illegally accessing reflective code. Lower level code
             // accesses illegal reflective code on behalf of this code.
             m = o.getClass().getMethod("add", new Class[] { Object.class, });
             o = m.invoke(o, new Object[] { button1, });
System.err.println("o="+o);
         } catch (Exception x) {
             x.printStackTrace();
         }

         javafx.scene.Scene scene0 = new javafx.scene.Scene(vbox);
         stage.setScene(scene0);
         stage.show();
     }

}

/*
# compile:
javac ReflectAccessBug.java

# run:
java ReflectAccessBug
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by ReflectAccessBug (...) to method 
com.sun.javafx.collections.VetoableListDecorator.add(java.lang.Object)
WARNING: Please consider reporting this to the maintainers of ReflectAccessBug
WARNING: Use --illegal-access=warn to enable warnings of further illegal 
reflective access operations
WARNING: All illegal access operations will be denied in a future release
*/

---------- END SOURCE ----------

Reply via email to