PROPOSAL: Using Ordinary Syntax for Reflective Method Invocations

Alan Snyder javalists at cbfiddle.com
Fri Mar 27 12:25:44 PDT 2009


Using Ordinary Syntax for Reflective Method Invocations


AUTHOR: Alan Snyder

OVERVIEW

FEATURE SUMMARY:

This proposal allows ordinary method invocation syntax to be used to issue
a reflective method invocation, when enabled by the use of a new marker
type for the target object.

MAJOR ADVANTAGE:

Invoking a method using the reflection API is tedious and verbose, and it
does not look anything like the corresponding ordinary method invocation
that would be used when the target class or interface is statically known.
The proposal allows the ordinary method invocation syntax to be used
instead to invoke a method using reflection.

MAJOR BENEFIT:

This proposal makes invoking methods using reflection much easier to write
and also much more readable.

When a programmer wants to avoid creating a static dependency on an
optional module, one way to do so is to invoke the module using
reflection. (Note that the target interface may be packaged with the rest
of the module, so even a static reference to the target interface would
create a dependency.) This proposal would make such loose couplings much
simpler and more natural, and would also make it easier to change from
tight coupling to loose coupling, or vice versa.

MAJOR DISADVANTAGE:

A small language change is required, involving the introduction of a new
marker type and an extension of method invocation semantics to cover the
new marker type.

ALTERNATIVES:

The major alternatives are to use an IDE or a (nonstandard) preprocessor
to generate the appropriate calls on the reflection API. IDE generated
code is easy to write but remains difficult to read. Preprocessors do not
interact well with tools, especially debuggers.

EXAMPLE:

Here is a simple invocation of a method on an object whose type is not
statically known. The type Unknown is the new marker type that signals the
use of reflection to invoke methods on the object so declared.

    Unknown o = ...;  // obtain object without referencing its class
    try {
      String s = (String) o.getName("Foo", 3);
    } catch (InvocationTargetException ex) {
      ... method threw an exception
    } catch (Exception ex) {
      ... could not find method, invoke method, or wrong result type
    }

A more complex example might need to use a type cast to provide a type for
a null argument:

    String s = (String) o.getName((String) null, 3);

Another complex example would handle the various individual exceptions
that can be raised:

    try {
      String s = (String) o.getName("Foo", 3);
    } catch (NoSuchMethodException ex) {
      ...
    } catch (SecurityException ex) {
      ...
    } catch (IllegalAccessException ex) {
      ...
    } catch (ExceptionInInitializerError ex) {
      ...
    } catch (InvocationTargetException ex) {
      ...
    } catch (ClassCastException ex) {
      ...
    }

DETAILS

The proposal requires the introduction of a new marker type, which I am
calling Unknown. It could be new interface, perhaps in java.reflect. My
thought is that extending this interface probably should not be allowed,
as it would seem pointless.

The Unknown type is used by the programmer to indicate a desire to perform
reflective invocations on an object whose type is not statically known. It
is thus different than Object (despite having the same set of declared
methods), where no such intent is implied. Thus, there is no relaxation of
static type checking in programs that do not explicitly use this new
feature.

When the compiler encounters a method invocation where the static type of
the target object expression is Unknown, it replaces the static invocation
with the equivalent reflection code, something like this:

    {
      Class c = o.getClass();
      Class c1 = String.class;
      Class c2 = Integer.class;
      Method m = c.getMethod("getName", c1, c2);
      Object arg1 = "Foo";
      Object arg2 = 3;
      String s = (String) m.invoke(o, arg1, arg2);
    }

The Class objects used in the method lookup are determined based on the
expression types of the invocation argument expressions, not the runtime
types of the argument objects. (The latter would fail to handle null
arguments, for example.) As in the above example, the null literal could
not be used without a type cast. I do not claim to have full command of
the type system vocabulary, but it seems to me that the process is roughly
equivalent to creating a class literal based on the result of type
erasure.

SPECIFICATION:

JLS 15.12.1 would need to be changed to recognize this new case.
Specifically, this clause:

    If the form is Primary.NonWildTypeArgumentsopt Identifier, then the
name of the method is the Identifier. Let T be the type of the Primary
expression; then the class or interface to be searched is T if T is a
class or interface type, or the upper bound of T if T is a type
variable.

would be extended as follows:

    If the form is Primary.NonWildTypeArgumentsopt Identifier, then the
name of the method is the Identifier. Let T be the type of the Primary
expression; then the class or interface to be searched is T if T is a
class or interface type, or the upper bound of T if T is a type
variable. However, if T is the interface Unknown, no search of that
interface is performed. Instead, the method is determined at runtime
by examining the class of the target object.

Since the JLS does not define reflection, it is not clear to me whether
the exact details would be specified in the JLS.

COMPILATION:

The compiler would have to implement this new case. I don't believe there
are any new concepts involved. Debuggers should be changed to make the
invocation look like an ordinary invocation.

COMPATIBILITY

The major compatibility issue arises from the introduction of a new name
in the class name space. That could potentially cause a program to fail to
compile if it imported a class with the name Unknown using a wild card
import statement.





More information about the coin-dev mailing list