JDK-4810162 : Make reflection more convenient to use
  • Type: Enhancement
  • Component: specification
  • Sub-Component: language
  • Affected Version: 1.4.1
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux
  • CPU: x86
  • Submitted: 2003-01-29
  • Updated: 2008-04-01
  • Resolved: 2008-04-01
Related Reports
Duplicate :  
Relates :  
Description

Name: nt126004			Date: 01/29/2003


FULL PRODUCT VERSION :
Java(TM) 2 Runtime Environment, Standard Edition (build
1.3.1-root_1.3.1_020714-12:46)
Java HotSpot(TM) Client VM (build 1.3.1_03-69, mixed mode)

A DESCRIPTION OF THE PROBLEM :
Reflection is a powerful tool which makes Java into a far
more dynamic language than it would be without it.  It makes
possible an entirely different style of programming, in
which Field and Method objects are widely passed around
between methods, serving a function analogous to that of
pointers and function pointers in other languages.

Unfortunately, it only makes this possible, not convenient.
 The current reflection API is far more cumbersome than it
has any reason to be, and this has prevented it from being
used nearly as heavily as such a powerful tool really ought
to be.

I therefore recommend that the reflection API be revised to
make it far more convenient to use.  This involves four
specific recommendations.  In describing the
recommendations, the following code will be used as an example:

public class UsefulObject
{
  public int aField;
  public static int staticField;
  public void greet(String name)
  {
    System.out.println("Hello "+name);
  }
}
UsefulObject obj = new UsefulObject();

Here are my recommendations.

1. Create a simple syntax for obtaining and using Field and
Method objects.

This requires the compiler to automatically convert the new
syntax into the equivalent method calls.  The following
pairs of lines show a line in the suggested syntax, and the
equivalent code it would be converted to.

Field f = UsefulObject::aField;
Field f = UsefulObject.class.getField("aField");

Method m = UsefulObject::greet(String);
Method m = UsefulObject.class.getMethod("greet", new Object
[] {String.class});

int i = obj->f;
int i = f.get(obj);

obj->f = 5;
f.setInt(obj, 5);

obj->m("Fred");
m.invoke(obj, new Object [] {"Fred"});

This would require changes to the compiler, but should not
involve any significant changes to the JVM itself.

2. Define classes to represent fields and methods of
specific objects.

Field and Method objects are generic: they are not tied to
any particular object, and you must specify a target object
in order to evaluate or invoke them.  I suggest that new
classes be defined which represent specific fields and
methods of specific objects.  I will refer to these classes
as "FieldInstance" and "MethodInstance", although there are
probably better names for them.

The list of methods for the FieldInstance class would be
something like this:

public FieldInstance(Field field, Object object);
public Object get();
public void set(Object value);
public Field getField();
public Object getObject();

The MethodInstance class would be analogous, but with an
invoke() method in place of get() and set().

Of course, these classes would be trivial to implement in
pure Java.  To be really convenient, however, there should
be a simplified syntax for using them similar to the one
described above:

FieldInstance fi = obj::aField;
MethodInstance mi = obj::greet(String);
FieldInstance fi2 = UsefulObject::staticField;

The third line is a debatable case.  Static fields and
methods do not require you to provide an object to access or
invoke them.  Semantically, it therefore makes more sense to
represent them with FieldInstances instead of Fields.  From
a syntactic point of view, one could argue that it is less
confusing if the :: operator always produces a Field when
applied to a class, whether or not the field is static.

3. Relax the access controls for reflection.

Once you start passing around Field and Method objects
between methods, you soon find yourself wanting to pass
references to the methods of anonymous inner classes.  When
the receiving code tries to invoke the method, however, it
receives an IllegalAccessException.  Method.invoke() throws
an exception if either of the following conditions is met:

The method being invoked is not public

or

The method belongs to a non-public class

The latter condition is excessive and unnecessary.
Presumably it was put there based on the philosophy that if
you cannot invoke a method without reflection, you should
not be able to invoke it with reflection either.  That is a
very poor reason: the whole point of reflection is to let
you do things you could not do otherwise.  As long as a
method is public, any piece of code which has obtained a
reference to it should be free to invoke it.

A much more reasonable place to enforce class level access
restrictions would be at Class.getMethod().  It could throw
an exception if the invoking code did not have permission to
access the class.  Or it could be enforced by
Class.forName() to prevent the Class object from being
obtained in the first place.  Either way, if a class which
*does* have access to the method in question chooses to give
the Method object to another class, that other class should
be free to invoke it so long as the method itself is public.
 Otherwise, it is impossible to use anonymous inner classes
as function callbacks in this way.

4. Make reflection related exceptions be RuntimeExceptions.

When used to its fullest potential, reflection will become
pervasive.  I therefore recommend that the following
exceptions become subclasses of RuntimeException so they
will not need to be explicitly thrown by nearly every method:

NoSuchFieldException
NoSuchMethodException
IllegalAccessException
InvocationTargetException

REPRODUCIBILITY :
This bug can be reproduced always.
(Review ID: 180506) 
======================================================================

Comments
EVALUATION Partly, what's requested here is a scripting language which can abstract over Java members. The idea of scripting langauges on top of the JVM has come up in some form many times over the years. Making this language a sublanguage of Java, as suggested, is not desirable. It is a very poor fit with Java - it is (very) dynamically typed, contrary to the sprit of the Java programming language. It's performance charcteristics would be very different. No doubt a great many niggling issues of semantic and syntactic compatibility would arise as well. One could implement this as a separate language that interoperated with Java, in a one-way version of what is termed symbiotic reflection. I don't expect Java to incorporate this, but anyone can implement it as a separate tool. ###@###.### 2003-01-29
29-01-2003