Home > Articles > Programming > Java

This chapter is from the book

3.3 Reflective Invocation

The Reflection API's ability to report the fields, methods, and constructors of a class is a very useful feature for the authors of Java development environments. For example, Java IDE wizards can automatically generate empty implementations of an interface for you to fill in. Java reflection makes this simple, whereas with many other languages, the development environment must build its own custom metadata representation to provide these services. While reflective reporting is a nifty parlor trick, the real power of reflection lies elsewhere, in the invocation APIs. With reflective invocation, you can access or change the value of a field and you can even invoke a method without any compile-time knowledge of the classes involved.

The invocation portion of the Reflection API uses the Field, Method, and Constructor classes to query and modify the state of Java classes and instances at runtime. This ability can be used to emulate function pointers, which do not exist in Java. Invocation can also be used as a substitute for inheritance. Instead of casting an object to a known interface type, you simply reflect against the class to see if it implements a particular method. Reflective invocation is also critical in crossing class loader boundaries. If you have a reference to a type that is not visible to your class loader delegation, you can use reflection to access that type's fields, constructors, and methods.

The reflective invocation APIs share several common elements. Because they are invoked on instances of Field, Method, or Constructor, they do not have an implicit this reference to the actual type being modified, and they must specify the this reference as an explicit parameter. Because reflective invocation APIs do not know the parameter types in advance, parameters are specified as Object or arrays of Object. Similarly, return types are always of type Object. Finally, reflective invocation cannot know in advance what checked exceptions might be thrown by a method, so all exceptions are caught by the APIs and rethrown as some wrapper type. The key reflective invocation APIs are summarized in Listing 3–13.

Listing 3–13 Key Reflective Invocation APIs

//all invocation APIs may throw
//IllegalArgumentException, IllegalAccessException

//from java.lang.reflect.Field
public Object get(Object this);
public void set(Object this, Object value);

//from java.lang.reflect.Method
public Object invoke(Object this, Object[] args)
    throws InvocationTargetException

//from java.lang.reflect.Constructor
public Object newInstance(Object[] args)
    throws InstantiationException, 
       InvocationTargetException

3.3.1 A Reflective Launcher

As an example of a situation in which the Reflection API is essential, consider the RunFromURL class shown in Listing 3–14. RunFromURL is very similar to the Java launcher except that it loads a class from a URL passed on the command line instead of from the classpath. The meat of the code is the run method, which uses getMethod to find a method with the signature main(String[] args) and then invokes that method with a null reference for this (since the method is assumed to be static)3 and an array of arguments from the command line. RunFromURL can execute the main method of any arbitrary class. Moreover, reflection is the only way this can be accomplished. RunFromURL cannot directly reference the class to be loaded. If RunFromURL referenced the class directly, implicit class loading would cause the class to be loaded from the classpath, which would defeat RunFromURL's ability to dynamically load classes from a location chosen at runtime.

Listing 3–14 RunFromURL

//imports, error checking removed for brevity
public class RunFromURL {
 public static void run(String url, String clsName,             
    String[] args) throws Exception 
 {
  URLClassLoader ucl = new URLClassLoader(new URL[]            
     {new URL(url)});
  Class cls = ucl.loadClass(clsName);
  Class argClass = String[].class;
  Method mainMeth = cls.getMethod("main", 
           new Class[]{argClass});
  mainMeth.invoke(null, new Object[]{args});
 }
 public static void main(String [] args) throws Exception
 {
  int argCount = args.length-2;
  String[] newArgs = new String[args.length-2];
  for (int n=0; n<argCount; n++) {
   newArgs[n] = args[n+2];
  }
  run(args[0], args[1], newArgs);
 } 
}

3.3.2 Wrapping Primitive Types

The syntax shown so far works fine for object types, but what about methods and fields that utilize primitive types? Java includes a set of primitive types (short, long, byte, boolean, double, float, int, and char) that can be represented directly in memory without the overhead of extending Object. These types pose a problem for reflection because the invocation APIs have generic signatures based on Object and Object[]. To enable the use of primitive types in reflective invocation, each primitive type in Java has a corresponding immutable class in the java.lang package. If you want to use reflection to call a method that takes a primitive type, you must use an instance of one of the wrapper classes instead. So for a hypothetical method

 int add(int n1, int n2);

you would use the following reflective syntax:

//to reflectively add ints n1 and n2
Object[] args = new Object[]{new Integer(n1),
               new Integer(n2)};
Integer temp = (Integer) addMethod.invoke(args);
int result = temp.intValue();

Before calling add, you must "box" the int parameters as Integers. Then, you must take the result of the call, cast it to Integer, and then "unbox" it to an int. This is inconvenient but necessary given the dual nature of numeric types in Java.

A related issue is the process of requesting a method with primitive types in its signature. Given that reflective invocation must use Integer wherever int was originally specified, how can getMethod distinguish between the two signatures shown in Listing 3–15? Even though both of these methods would be reflectively invoked with the same argument types, there is a way to distinguish between them to getMethod and related methods. Integer, like any other Java class, can be represented by its corresponding pseudo-literal, in this case Integer.class. For reflection purposes, the primitive types, such as int, have a special Class representation that is the static TYPE constant of the corresponding wrapper class. So, you should use Integer.class to request an Integer but Integer.TYPE to request an int, as Listing 3–15 demonstrates.

Listing 3–15 Finding Methods That Use Primitive Types

//two methods differing only in wrapper vs. primitive
public Integer add(Integer n1, Integer n2);
public int add(int n1, int n2);
 
//to access the wrapper version
Class[] wcls = new Class[]{Integer.class,Integer.class};
Method methWrapper = cls.getMethod("add", wcls);

//to access the primitive version
Class[] pcls = new Class[]{Integer.TYPE,Integer.TYPE};
Method primitiveWrapper = cls.getMethod("add", pcls);

3.3.3 Bypassing Language Access Rules

The rules for dealing with primitive types are arbitrary but raise no interesting design questions. A more challenging design issue is deciding how to enforce language access rules during reflective invocation. In normal Java programming, member access is controlled by the public, private, and protected keywords, plus the implied package-private setting if no keyword is specified. If you attempt to access a member whose access is restricted, compilation will fail. With reflective invocation, there is no way to know the protection modifier of a method at compile time. At runtime, the virtual machine can use class metadata to determine the protection modifier, and it can possibly prevent the operation being attempted.

If a piece of code attempts to use reflection to access a member that it would not have been allowed to access from compiled Java code, should the operation be allowed to proceed? The initial intuition is "no!" There are at least two good reasons to enforce the language protection rules during reflective invocation. First, reflection should not act as a back door to compromise encapsulation. It is both a design flaw and a security risk to open private members to code outside of a class. Second, reflection should obey the common-sense rule of least surprise. If the language works a certain way during normal method invocation, reflective invocation should mimic that behavior to the extent possible.

3.3.3.1 Using setAccessible

Despite these arguments in favor of limiting reflection, reflective code sometimes needs to bypass Java's protection modifiers in order to implement low-level subsystems that provide services for arbitrary Java classes. For example, one use of the Reflection API is for generic persistence services for instances. A generic persistence layer that did not persist the entire state of an object, including private members, would not be very generic. However, the design of the Reflection API also addresses the concerns raised previously about blithely accessing private data. The rules for language-level access checks provide something for everybody:

  1. By default, reflective invocation does not bypass language access checks. If you attempt to access a private, package-private, or protected member that you could not access normally, reflection will fail with an IllegalAc-cessException. This preserves the rule of least surprise.

  2. If you want to bypass language-level access checks, you can request this ability by calling setAccessible(true) on an AccessibleObject (see Listing 3–16). The reflection classes Field, Method, and Constructor all extend AccessibleObject. This allows a service like an XML serializer to gain access to all of an object's state.

Listing 3–16 AccessibleObject

// from java.lang.reflect.AccessibleObject
package java.lang.reflect;
public class AccessibleObject {
 //request permission to bypass access checks
 public void setAccessible(boolean flag) 
       throws SecurityException;
 //bypass access checks for several items in one shot
 public static void setAccessible(AccessibleObject[]
           arr, boolean flag);
 //remainder omitted for clarity
}

You can control which code is allowed to call setAccessible by installing a SecurityManager for a process.4 The setAccessible method provides a convenient chokepoint for a security check, and the default security implementation will prevent application code from calling setAccessible, while allowing system code to do so.

To see these rules in action, consider a Reporter's attempts to discover a Superhero's secret identity, shown in Listing 3–17. The Reporter wants to retrieve the Superhero's secretIdentity field via reflection, thus bypassing the fact that the field is private. If you execute this code as is, it will fail with an IllegalAccessException. In order to bypass the language check, add a call to secret.setAccessible(true) before the call to secret.get(s). With this call in place, reflection will bypass the language access check and return the private field. However, if you turn security on with the java.security.manager flag, the call to setAccessible will fail with an AccessControlException as it does here:

 >java –cp classes –Djava.security.manager Reporter
java.security.AccessControlException: access denied
(java.lang.reflect.ReflectPermission suppressAccessChecks)

Listing 3–17 Misusing Reflection to Access a Private Field

public class Superhero {
public class Superhero {
 public final String name;
 private final String secretIdentity;
 public Superhero(String name, String secretIdentity) {
  this.name = name;
  this.secretIdentity = secretIdentity;
 }
}
public class Reporter {
 public static void main(String [] args) 
     throws Exception {
  Superhero s = new Superhero("ReflectionMan", 
                "Brian Maso");
  hackIdentity(s);
 }
 public static void hackIdentity(Superhero s)
           throws Exception {
  Field secret = Superhero.class.
          getDeclaredField("secretIdentity");
  System.out.println("Identity is " + secret.get(s));
 }
}

To call setAccessible when security is enabled, you must have the suppressAccessChecks permission. By default, code that is in the core API or the extensions directory will have the suppressAccessChecks permission and be able to perform services such as serializing an object's private state. Application code loaded from the classpath or via a URLClassLoader will not have this permission, and therefore, it will be unable to inadvertently manipulate an object's implementation details.

3.3.3.2 Reflective Modification of Final Fields

Given that reflection sometimes has a legitimate reason to modify private fields, it is also logical to consider whether reflection might need to modify final fields. Before you look at how Java actually handles this today, consider the problem. Imagine a Person class that wanted to have some immutable fields:

public class Person {
 private final String name;
 private final String socialSecurityNo;
 //etc...
}

Now consider what would happen if you wanted to write a generic mechanism to instantiate Person from a row in a database. There are three approaches that might work:

  1. You could use reflection to instantiate a Person and then assign its fields. However, since some of the fields are final, this would require that the reflection APIs be designed with the ability to modify final fields. Many people argue that this ability is counter to the very definition of final.

  2. You could use a native method to instantiate a Person and then assign its fields. The Java Native Interface (JNI) includes APIs to do all sorts of things that would be illegal in Java, including instantiating objects without running their constructors and modifying final fields. The major downside to this approach is that you have to develop and deploy native code.

  3. You could require that the Person class provide an "all-fields constructor" that would simply pass through an initial value for every single field in the object. Generic services, such as the hypothetical database code, would use reflection to invoke this special constructor. The constructor probably would not be used during normal operation, and in fact, you could mark it private to guarantee this. This approach has neither of the disadvantages of the first two. Because constructors are allowed to set final fields, reflection does not need the ability to subvert final semantics. Also, no native code is required. The problem with this approach is that it requires the author of a class to explicitly write a special constructor in order to use serialization. The first two approaches simply leverage metadata and require no assistance from the classes that want generic services.

None of these solutions is ideal, and the Java language is still evolving in this area. Prior to SDK version 1.3, the first approach was used. The setAccessible method gave permission to modify final fields, and APIs such as serialization leveraged reflection. This led to some very confusing situations, as demonstrated in Listing 3–18.

Listing 3–18 Reflectively Modifying final Fields Is a Bad Idea.

import java.lang.reflect.*;
public class LimitedInt {
 public static void main(String[] args) throws Exception
 {
  System.out.println("Integer.MAX_VALUE=" + 
            Integer.MAX_VALUE);
  Field mv = Integer.class.getField("MAX_VALUE");
  mv.setAccessible(true);
  mv.set(null, new Integer(42));
  System.out.println("Retest: MAX_VALUE=" + 
            Integer.MAX_VALUE);
  System.out.println("Reflective: MAX_VALUE=" + 
            mv.get(null));
 }
}

Prior to SDK 1.3, setAccessible worked for final fields, and it was actually possible to change Integer.MAX_VALUE! This was made doubly confusing because Java compilers typically optimize static final primitive type declarations by inlining any access. So, even though you could change the value of MAX_VALUE seen by reflection, you could not change the value of MAX_VALUE where that constant was referenced in a class. On SDK 1.2, the code in Listing 3–18 prints the following:

 Integer.MAX_VALUE=2147483647
Retest: MAX_VALUE=2147483647
Reflective: MAX_VALUE=42

SDK 1.3 removed reflection's ability to change final fields. In the case of static final fields, this was definitely a step in the right direction. However, this also broke the approach to serialization that leveraged reflection to populate instance fields for classes like Person. In order to get around this new limitation, services like serialization switched to the second approach detailed earlier, using native methods to populate fields. This is not likely to be the final word on the subject. The proposed changes to Java's memory model would remove even the ability of native code to modify final fields (see [JMM] for details). If the new model is adopted, then the third approach might become necessary; this would require class authors to provide a special constructor for serializable classes that have final fields.

3.3.4 Exceptions Caused by Reflective Invocation

As mentioned above, attempts to bypass language restrictions without calling setAccessible(true) will generate an IllegalAccessException. IllegalAccessException is one of several exceptions that reflective invocation throws to indicate a problem that normal code would catch at compile time. Another example is IllegalArgumentException, used to indicate that a method argument is invalid. This will occur if one of the Objects in the array passed to invoke cannot be converted to the correct type for the underlying method. If you try to use reflection to instantiate an abstract class, Class will throw an InstantiationException.

Taken together, these three exception types are the reflection equivalent of the compiler's enforcement of language rules. While setAccessible can bypass the access rules, there is no way to get around the situations that cause an IllegalArgumentException or an InstantiationException. You must always pass arguments of the correct type, and you can never instantiate abstract classes.

Another situation where reflection may throw an exception is if the underlying method or constructor throws an exception. If this occurs, one of three things will happen:

  1. If a method throws an unchecked exception, such as an Error or Run-timeException, then this exception will be passed unmodified through the reflection layer to the caller. This behavior is no different from ordinary method invocation, where unchecked exceptions do not require an explicit catch block.

  2. A method may throw a declared checked exception. This is a problem for reflection because checked exceptions must be declared in a method signature, and reflection does not know a method's signature at compile time. The reflection APIs catch checked exceptions and convert them into a checked type InvocationTargetException that is declared in the signature of Method.invoke. This situation occurs frequently and leads to a special idiom on the part of the caller, as shown in Listing 3–19. InvocationTargetException is a reflection-provided wrapper class that can access the original exception via getTargetException. A client is typically more interested in the original exception, which indicates what went wrong, than it is in the InvocationTargetException, which only indicates that reflection was involved. As the sample shows, reflective clients will frequently catch InvocationTargetException, extract the original exception, cast it to some type expected by the caller, and rethrow.

  3. A method may throw a checked type that it did not declare. If this happens, invoke will convert the exception into the unchecked type UndeclaredThrowableException. This situation is extremely rare since the compiler will prevent a class from compiling if it tries to throw a checked exception that it did not declare. UndeclaredThrowableException indicates either a version mismatch caused by a partial recompile, or a corrupted binary class.

Listing 3–19 Extracting the "Real" Exception

//invokes some I/O method unknown at compile time
public void reflectiveIO(Method m, Object this, 
      String fileName) throws IOException
{
 try {
  m.invoke(this, new Object[]{filename});
  }
 catch (InvocationTargetException ite) {
  throw (IOException) ite.getTargetException(); 
 }
}

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020