The dynamics of Java programming, part 3rd: Application Reflection--Reprint

Source: Internet
Author: User
Tags stack trace

In last month's article, I introduced the Java Reflection API and briefly described some of its basic features. I also examined the performance of reflection and gave some guidelines at the end of the article to tell the reader when to use reflection in an application and when not to use reflection. In this month's article, I'll look at an application to discuss the issue in more depth, which is a library for command-line parameter processing, and it's a great way to reflect the strengths and weaknesses of reflection.

In the beginning, before I actually get into the job of writing implementation code, I'll first define the problem to be solved and then design an interface for the library. However, in developing this library, I did not follow the above steps-I first tried to simplify the existing code in a group of applications that have a common code base, and then make it generic. This linear sequence of "definition-design-build" used in this article is much more concise than describing the development process completely, and, by organizing the description of the development process in this way, I can revise some of my assumptions and clear out unnecessary aspects of the library's code. It is entirely a hope that you will find that this approach works well as a model for developing your own reflection-based applications.

Defining issues

I have written many Java applications that use command-line arguments. At first, most of the applications were small, but eventually some applications became so big that I was surprised. Here are the standard patterns I've observed for the larger process of these applications:

    1. Initially there is only one or two parameters, arranged in a particular order.
    2. Given that the application has more to do, add more parameters.
    3. Tired of entering all the parameters each time, let some parameters be optional parameters, so that these parameters with the default values.
    4. Forgetting the order of the parameters, modify the code to allow the parameters to be arranged in any order.
    5. Give this app to someone else who is interested. But they do not know what these parameters represent, and then add more sophisticated error checking and "Help" descriptions for these parameters.

When I go to the 5th step, I usually regret not putting the whole process in the first step. Fortunately I will soon forget the later stages, less than two weeks, I will consider another simple small command-line program, I want to own this application. With this idea, the recurrence of the entire disgusting cycle is only a matter of time.

Some libraries can be used to help with command-line parameter handling. However, in this article I will ignore these libraries, instead of creating a library myself. This is not (or is not only) because I have an attitude of "not invented here" (that is, something that is not invented to be invented by outsiders), but because I want to take parameter processing as an example. As a result, the strengths and weaknesses of reflection reflect the need for a parameter processing library. In particular, the parameter processing library:

    • A flexible interface is required to support a variety of applications.
    • For each application, it must be easy to configure.
    • Top-level performance is not required because parameters are processed only once.
    • There is no access security issue because the command-line application typically does not have a security manager when it is running.

The actual reflection code in this library represents only a small part of the entire implementation, so I'll focus primarily on some aspects that are most relevant to reflection. If you want to find out more about this library (and perhaps you want to use it in your own simple command-line application), you can find a link to the Web site in the Resources section.

Draw up a piece of design

Perhaps the most convenient way for an application to access parameter data is through some fields of the application's main object. For example, suppose you are writing an application that generates a business plan. You may want to use a boolean tag to control whether the business plan is brief or lengthy, using one int as the first year of revenue, using one String as a description of the product. I'll call these variables that affect the running of the application, called formal parameters (parameters), to distinguish them from the values provided by the command-line arguments (arguments) -the parameters. By using fields for these parameters, you can make it easy to invoke them anywhere in the application code that requires the parameters. Also, if you use a field, it is convenient to set a default value for any parameter when defining the parameter field, as shown in Listing 1:

Listing 1. Business Plan Builder (partial inventory)
public class Plangen {    private boolean m_isconcise;          Rarely used, default false    private int m_initialrevenue = +;  Thousands, default is 1M    private float m_growthrate = 1.5;     Default is 50% growth rate    private String m_productdescription =//McD look out, here I come        "Efood-(Reall Y) Fast food Online ";    ...    private int revenueforyear (int year) {        return (int) (M_initialrevenue * MATH.POW (m_growthrate, year-1));    }    ...

Reflection will allow the application to directly access these private fields, allowing the parameter processing library to set the value of the parameter without any special hooks in the application code. But I do need some way to get this library to relate these fields to specific command-line arguments. Before I can define how this association between a parameter and a field communicates with the library, I need to decide how I want to format these command-line arguments.

For this article, I'll define a command-line format, which is a simplified version of UNIX conventions. The argument values of the formal parameters can be provided in any order, using a hyphen to indicate that an argument gives one or more single-character parameter tokens (as opposed to the value of the actual formal parameter). For this business plan generator, I will use the following formal parameter tag characters:

    • C--Brief plan
    • F--First annual income (Thousands of United States dollars)
    • G-Growth rate (per annum)
    • N--Product name

booleanA parameter can set a value by simply marking the character itself, while other types of parameters require some additional argument information. For a numeric argument, I just immediately follow the parameter tag character (which means that the number cannot be used as a marker character), and for a parameter with String a type value, I will use the argument following the tag character as the actual value on the command line. Finally, if you need some formal parameters (such as the file name of the output file for the Business Plan generator), I assume that the argument values of these parameters follow the optional parameter values in the command line. With these conventions given above, the command line of the business Plan generator looks like this:

java PlanGen -c -f2500 -g2.5 -n "iSue4U - Litigation at Internet Speed" plan.txt

If you put it together, the meaning of each argument is:

    • -c--Generate a brief plan
    • -f2500--The first annual income is $2,500,000
    • -g2.5--annual growth rate of 250%
    • -n "iSue4U . . ."--The product name is "Isue4u ..."
    • plan.txt--Required output file name

At this point, I have got the specification of the basic function of the parameter processing library. The next step is to define a specific interface for this application code to use this library.

Back to top of page

Select interface

You can use a single call to handle the actual processing of command-line arguments, but the application first needs to define its specific parameters to the library in some way. These parameters can have different types (for example, for business plan generators, the type of formal parameters can be boolean , int、 float and java.lang.String ). Each type may have some special needs. For example, if a tag character is given, it is better to define the parameter, rather boolean false than always defining it as true . Also, it is int useful to define a valid range for a value.

My approach to dealing with these different requirements is to first use a base class for all formal parameter definitions and then fine-classify the base class for each specific type of formal parameter. This approach allows an application to provide a formal parameter definition to the library in the form of an instance array of the basic parameter definition class, while the actual definition can use subclasses that match each type of formal parameter. For an example of a Business plan generator, this can take the form shown in Listing 2:

Listing 2. Formal parameter definitions for business plan generators
private static final parameterdef[] Parm_defs = {     new booldef (' C ', "M_isconcise"),    new Intdef (' F ', "m_ Initialrevenue ", 10000),    new Floatdef (' G '," m_growthrate ", 1.0, 100.0),    new Stringdef (' n '," m_ ProductDescription ")}

With the parameters that are allowed to be defined in an array, the application's invocation of the parameter-handling code can be as simple as a single call to a static method. To allow an extra argument (either a required value or a variable-length value) other than the one defined in the parameter group, I will return the call to the actual number of processed arguments. This allows the application to check for additional arguments and use them appropriately. The final result appears as shown in Listing 3:

Listing 3. Working with libraries
public class plangen{private static final parameterdef[] Parm_defs = {...}; public static void Main (string[] args) {//If no arguments be supplied, assume help is needed if (args            . length > 0) {//process arguments directly to instance Plangen Inst = new Plangen ();                        int next = Argumentprocessor.processargs (args, parm_defs, inst); Next unused argument is output file name if (next >= args.length) {System.err.println ("Mi                ssing Required output file name ");            System.exit (1);            } File Outf = new file (args[next++]); ...        } else {System.out.println ("\nusage:java Plangen" + "[-options] file\noptions are:\n C Concise PL        an\n "+" F first year Revenue (k$) \ n g growth rate\n "+" N Product description "); }    }}

The last remaining part deals with error reporting (such as an unknown parameter marker character or an out-of-range numeric value). For this purpose, I will define the exception ArgumentErrorException as an unchecked exception that will be thrown if there is an error of this type. If the exception is not captured, it will close the application immediately and output an error message and stack trace to the console. An alternative is to catch the exception directly in your code and handle the exception in other ways (for example, you might output a real error message with the use of information).

Back to top of page

Implementing libraries

In order for the library to use reflection as planned, it needs to find some of the fields specified by the parameter definition array, and then save the appropriate values in the fields from the corresponding command-line arguments. This task can be handled by locating only the field information needed for the actual command-line arguments, but instead I choose to separate the lookup and use. I'll find all the fields in advance and use only the information that has been found during parameter processing.

Finding all the fields in advance is an error-proof programming step that eliminates a potential problem with reflection. If I'm just looking for a field that I need, it's easy to break a parameter definition (for example, the wrong field name), and you can't recognize that an error has occurred. There is no compile-time error, because the field name is passed as a String  pass, and the program can perform well as long as the command line does not specify an argument that matches a broken formal parameter definition. This blinding error can easily lead to the release of imperfect code.

Assuming I want to look up field information before actually working with the arguments, listing 4 shows the implementation of the base class for the parameter definition, which is implemented with a bindToClass() method for working with field lookups.

Listing 4. base class for formal parameter definitions
public abstract class parameterdef{protected char M_char;        Argument flag character protected String m_name;        Parameter field name protected field M_field;        Actual parameter field protected parameterdef (char Chr, String name) {M_char = CHR;    M_name = name;    } public char Getflag () {return m_char; } protected void Bindtoclass (Class clas) {try {//Handle the field look up and accessibilit            Y M_field = Clas.getdeclaredfield (m_name);                    M_field.setaccessible (TRUE); } catch (Nosuchfieldexception ex) {throw new IllegalArgumentException ("Field '" + m_name + "' N        OT found in "+ clas.getname ()); }} public abstract void handle (Argumentprocessor proc);} 

The actual library implementation also involves several classes that are not mentioned in this article. I'm not going to introduce each of these classes, because most of them are not related to the reflection aspect of the library. What I'm going to mention is that I chose to save the target object as ArgumentProcessor a field in the class and implement the true setting of a formal parameter field in this class. This approach provides a simple pattern for parameter handling: ArgumentProcessor class scan arguments to discover parameter markers, find the corresponding parameter definition for each tag (always ParameterDef a subclass), and then call the method of the definition handle() . handle()method is called after the argument value is interpreted ArgumentProcessor setValue() . Listing 5 shows an incomplete ArgumentProcessor version of the class, including parameter binding calls in the constructor and setValue() methods:

Listing 5. Partial inventory of the main library class
public class argumentprocessor{private Object m_targetobject;     Parameter value object private int m_currentindex; Current argument position ... public argumentprocessor (parameterdef[] parms, Object target) {//b IND all parameters to target class for (int i = 0; i < parms.length; i++) {Parms[i].bindtoclass (Targ        Et.getclass ());    }//Save target object for later use M_targetobject = target; public void SetValue (Object value, Field field) {try {//Set parameter Field value US                    ing reflection field.set (m_targetobject, value);                } catch (Illegalaccessexception ex) {throw new IllegalArgumentException ("Field" + field.getname () +        "is not accessible in object of class" + M_targetobject.getclass (). GetName ()); }} public void Reportargumenterror (char flag, String text) {throw new ARgumenterrorexception (text + "for argument" + Flag + "' in argument" + M_currentindex); } public static int Processargs (string[] args, parameterdef[] parms, Object target) {argumentprocess        or inst = new Argumentprocessor (parms, target); ...    }}

Finally, listing 6 shows a partial implementation of the formal parameter int definition subclass of the formal parameter value. This includes overloading the base class's bindToClass() methods (from Listing 4), which calls the implementation of the base class first, and then checks if the found field matches the expected type. Subclasses of other specific parameter types ( boolean、 float、 String ,, and so on) are very similar to this.

Listing 6. intFormal parameter definition class
public class Intdef extends parameterdef{private int m_min;              Minimum allowed value private int m_max;        Maximum allowed value public intdef (Char Chr, String name, int min, int max) {super (CHR, name);        m_min = min;    M_max = max;        } protected void Bindtoclass (Class clas) {super.bindtoclass (clas);        Class type = M_field.gettype (); if (type! = Integer.class && Type! = Integer.type) {throw new IllegalArgumentException ("Field '" + m_n        Ame + "' in" + clas.getname () + "was not of type int"); }} public void handle (Argumentprocessor proc) {//Set up for validating Boolean minus = Fals        E        Boolean digits = false;                int value = 0;        Convert number supplied in argument list to ' value ' ...//Make sure we have a valid value Value = minus?        -value:value; if (!digits) {pRoc.reportargumenterror (M_char, "Missing value");        } else if (Value < M_min | | value > M_max) {proc.reportargumenterror (M_char, "value out of range");        } else {Proc.setvalue (new Integer (value), M_field); }    }}

Back to top of page

End Library

In this article, I describe a design process for a library that handles command-line arguments as a practical example of reflection. This library is a good way to illustrate how to use reflection effectively-it simplifies the code for your application without sacrificing performance significantly. How much performance did it sacrifice? As can be seen from some quick tests of my development system, a simple test program runs on average only 40 milliseconds slower when using the entire library for parameter processing than without any parameter processing. Much of this time is spent on the loading of other classes used by library classes and library code, so even applications that define many command-line parameters and many argument values are unlikely to be much worse than the result. For my command-line application, the extra 40 milliseconds will not cause my attention at all.

The full library code can be found through the links in the reference resources. It includes some of the features I have not mentioned in this article, including some details such as hooks, which are used to easily generate a list of formatted formal parameter tags, as well as some descriptions that can help provide usage directives for your application. You are welcome to use this library in your own programs and extend the library in any way you find useful.

Now that I've talked about the basics of the Java class in part 1th, and the principles of the Java Reflection API in part 2nd, and the 3rd part, the remainder of this series will change the topic of bytecode processing, which is not familiar to us. In the 4th part, I'll start with an easy, first look at the user-friendly Javassist library for using binary classes. Do you want to convert the method, but don't want to start the program in bytecode? Javassist is just right for your needs. Next month we'll see how this can be achieved.

Original: http://www.ibm.com/developerworks/cn/java/j-dyn0715/index.html

The dynamics of Java programming, part 3rd: Application Reflection--Reprint

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.