Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Saturday, March 30, 2013

Best Practices in Java Exception Throwing, Handling, and Logging

Overview

Exception Related Terminology

In an object-oriented program language such as Java, C#, and JavaScript, an exception is an object that represents an abnormal event. When one method (caller) calls another method (callee), they may communicate about what happened via an exception:
  1. A method may throw an exception to let its caller know that an abnormal event occurred. We call it exception throwing. 
  2. A method may propagate an exception thrown by its callee to its own caller by declaring that exception in its throws clause of method declaration.
  3. A method may translate an exception thrown by its callee into another exception, and throw the new exception (to its own caller)
  4. A method may catch an exception thrown by, or propagated from, its callee, or an exception thrown by it. When a method catches an exception, it may do something in response to the exception. Doing something in response to an exception is called exception handling.
If an exception goes through the main method of a Java or C# program, the program crashes.

Why Throw and Handle Exception

Essentially, a method throws or propagates an exception to its caller to let it know something is wrong, and let the caller know it is time for it to do something to help (i.e. to handle the exception).
Obviously, in almost all cases, it is beneficial to handle exceptions to avoid:
  1. Program crashing
  2. Data corruption
  3. Resource hugging – hugging threads, unclosed database connections, unclosed socket connections, leaked memory, etc.
  4. No response or meaningless response to end users
  5. Any other ways to leave all or part of a system in invalid states.

Exception Throwing

Don’t Throw Exception When Nothing is Abnormal

Exception is supposed to be used to alert one’s caller that something is wrong and the caller should go through an exceptional flow path to handle it. Throwing an exception when nothing is abnormal just like raising fire alarm when you only try to tell your colleague that you need his/her help to make a copy. It works, but is wrong, confusing normal conditions with exceptional conditions. An exception handling flow path is not just another path. It costs much more in CPU time and other resources. It also confuses reader of the code, making it harder to see what the code really intents to do.

For more discussion, please see Item 57: Use exceptions only for exceptional conditions in Chapter 9. Exceptions in Joshua Bloch’s Effective Java, Second Edition.

Do Throw Exception When Something is Abnormal

In a programming language that does not have the exception support (i.e. the try/catch/finally construct), people tend to overload procedure (function, subroutine, method, etc) output (function return or output parameter) with error code. In such practice, certain values of the output are going to be interpreted as normal output and others as error code. There are many disadvantages in such practice:
  • It is more difficult to understand the procedures with output overloading of error code
  • The caller (client) must explicitly check the output to determine its appropriate response
  • Client code that handles the normal case and exception/error case tends to mingle together
The exception support in modern programming languages is just to overcome above problems. When we programming in a modern programming language, we should take advantage of its exception support and throw exception when something is abnormal.

Avoid Triggering Exception

When one (method) calls another method, the caller should be coded in such a way to avoid triggering exceptions thrown by the callee. If the callee does not throw exceptions, the caller don’t have to handle them, to propagate them, or to translate them. Following are ways to avoid triggering exceptions:
  • To avoid NullPointException,  make sure that an object is not null before invoke a method on it.
  • To avoid ClassCastException,  check type of the class to be casted using the instanceof operator before casting.
  • To avoid IndexOutOfBoundsException,  check the length of the array before trying to work with an element of it.
  • To avoid ArithmeticException, make sure that the divisor is not zero before computing the division.
In addition, frequently, a callee throws exception because arguments passed to it are not valid. To remedy such trouble, there should be good document about the callee in regard to what are valid arguments to it, and the caller should ensure the arguments are valid before pass them to the callee.
Similarly, end user input should be validate as early as possible, and the program should raise flag with sufficient information to the user as early as possible given certain user input are invalid rather than allowing the invalid input propagate down the stream further. The farther where a symptom shows up away from the root cause (here invalid user input), the harder to identify the real cause.

Checked or Unckecked Exception 

When one method throws an exception, that exception can be either a check exception or an unchecked exception. An exception that directly or indirectly extends java.lang.RuntimeException is an unchecked exception. An exception that directly or indirectly extends java.lang.Exception but not java.lang.RuntimeException is a checked exception. When one method throws a checked exception, it is mandatory for its caller to either catch the exception or propagates it to its own caller. All checked exception that a method may throw must appear in the throws clause of the method’s API. Initially it was thought best practice to only use checked exception because the benefits of easily seeing what exceptions can be thrown by a method and forcing the method’s callers to handle (or propagate) the exception. After many years of practices, a lot of Java programmers, include Joshua Bloch, Rod Johnson, and Bruce Eckel, came to realize that frequently a caller cannot really do much in handling exceptions thrown by its callee but is forced to have a certain amount of code to get around the mandatory. It costs developers’ time to add that code that doesn’t really do anything useful but clusters the code and confuses readers.

In short, my recommendation on determining between throwing checked or unchecked exception is as following:
  • A method should throw a checked exception when it can reasonably expect the caller to do something to recover from the exception.
  • A method should throw an unchecked exception when it can reasonably assumes the caller cannot do anything to recover from the exception
For more discussion on this issue, please see Item 58: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors and Item 59: Avoid unnecessary use of checked exceptions in Chapter 9. Exceptions in Joshua Bloch’s Effective Java, Second Edition.

The IBM DeveloperWorks article Java theory and practice: The exceptions debate also provides a summary of the checked v.s. Unckecked exception debates.

Exceptions Should Be In Terms of Caller’s Perspective

The purpose for a method to throw an exception is to let the caller know that something abnormal occurred. That something must be in the caller’s perspective, in other words, at the same abstraction level as other things in the implementation of the caller. For example, I am using a computer. In this case, I am the caller and the computer the callee. I “call” the computer to do many useful things for me. If suddenly the computer, instead of doing something I expected, shows me some message like F^&*(%$HNBVFER2U74^$4, I won’t have any clue about what is going on and what I should do in response. If, instead, the error message is “The second RAM module is corrupted”, then I know much better what is going on and what I should do in response. The first error message may mean something tor an electronics engineer specialized in RAM but is not at the level of abstractions known by an ordinary computer user.

For more discussion on this issue, please see Item 61: Throw exceptions appropriate to the abstraction in Chapter 9. Exceptions in Joshua Bloch’s Effective Java, Second Edition.

Exception Message

When one creates a new exception object, one passes a string message into the constructor. The message can be retrieve later to gain helpful information. It helps to have adequate information in the message to be written into log file and to help developers and administrators to reproduce and diagnose the exception later. Please note that such message is normally not for the caller to use because it is normally in natural language and it is hard for the caller (a program module) to understand.

For more discussion on this issue, please see Item 63: Include failure-capture information in detail messages in Chapter 9. Exceptions in Joshua Bloch’s Effective Java, Second Edition.

Document Exceptions Thrown in Javadoc

For each method that throws checked exceptions, in its Javadoc, document each exception thrown with a @throws tag, including the condition under which the exception is thrown.

For more discussion on this issue, please see Item 62: Document all exceptions thrown by each method in Chapter 9. Exceptions in Joshua Bloch’s Effective Java, Second Edition.

Exception Handling


When an exception is thrown by a method itself or its callee, the method may have to do one or more of the following to handle the exception:
  1. Do something to recover completely from the exception so its own caller does not have to notice the occurrence of the exception. It is ideal but rare.
  2. If the method cannot completely recover the system from the exception, do as much as possible to bring the system into a consistent state or roll it completely back to the original state, including release resources held for those operations that triggered the exception.
  3. If it is beneficial for its own caller to know the occurrence, it can re-throw the same exception, or wrap it in another exception and throws it as long as it is more appropriate. It is appropriate when the new exception can help its own caller to know better what happened from the caller’s perspective. Use Java 1.4 exception chaining mechanism to preserver root exception information.
  4. When it may help developers and system administrators on further investigating an exception and fixing the problem offline, log the exception, with stack trace, exception message,  and as much as possible details of the context (largely the state of “this” object and parameters to “this” method) where the exception is thrown by its callee or it. Most time, it is beneficial to do both logging and translation/wrapping (or re-throwing).

At lease a method at the top of the calling stack in that thread should log the exception to one or more log files. Usually only the method at the top of a calling stack in a particular thread should be allowed to halt the thread.

Recovering from Exceptions

In some cases, a method may recover from an exception. For example, inside a method, a trial has been made to open a network socket and an exception has occurred indicating that no socket could be opened. More trials can be made. If the later trial succeeded, the method recovered from its exception, and its own caller does not need to know the occurrence of the exception.

Mitigating Exception Impacts

In some cases, a method cannot completely recover from an exception. In such cases, the method should do as much as possible to:
  1. Bring the system into consistent state or the original state, so the system can keep running without corrupting data and late operations
  2. Release resources hold for the operations that triggered the exception. For example, in case of database exception, close the database connection to prevent database connection leaks. Other possible resources leaks are file handler leaks, socket leaks, memory leaks, etc. See more in the Release Resource in finally Block sub-section below. In such cases, most likely, the method should also need to let its own caller know the occurrence of the exception in order for its own caller to do its own share. If the original exception is equally at the same abstraction level of its own caller, re-throw the same exception. Otherwise, wrap the original exception into another exception that is at the abstraction level of its own caller and throw the other exception. Use Java 1.4 exception chaining mechanism to preserver root exception information.
For more discussion on this issue, please see Item 64: Strive for failure atomicity in Chapter 9. Exceptions in Joshua Bloch’s Effective Java, Second Edition.

Release Resource in finally Block

In case that a method needs to release resources hold no mater exceptions are thrown or no, release the resources in the finally block of try/catch/finally construct. The finally block will be executed no matter the method finishes normally or with exception.


No Blank Catch Clause

In general, it is a very bad practice to have blank catch clause for exceptions. A blank catch clause does not really recover or mitigate an exception but prevents other from doing so. Only for rare cases, such practice is acceptable. Acceptable cases are:
  • exception during close JDBC ResultSet, Statement, and database connection
  • exception during close file, input or output stream, network socket
  • InterrupedException during thread sleeping

Example:
try {
    Thread.sleep(1000);
} catch (InterruptedException ex) {
    // log the exception as a warning
}

Even in those cases, it is better to log the exception for later investigation.

For more discussion on this issue, please see Item 65: Don't ignore exceptions in Chapter 9. Exceptions in Joshua Bloch’s Effective Java, Second Edition.

Exception Logging


Exception logging is for developers and system administrators to diagnosis the root problems and fix them. The logged information will be used offline. It is different from exception recovering and mitigating, which are done at runtime. Exceptions may be caused by incorrect user input or actions. There is almost nothing for developers and system administrators to do to fix the problems. Logging for such exceptions are not needed. Exceptions may be caused by limitations of system or network capacity, or problems of external systems. Logging such exceptions may help system administrators to have better idea about the capacity limitations and to plan for adding resources, or to communicate the problem with others who have control on the external systems. Exceptions may be caused by bugs in our own systems. Logging such exceptions is very helpful for developers to diagnose the root causes and fix them.

When log exception, try to include as much as possible information about the exceptions, include
  • Stack trace
  • Exception message
  • Data in the context – such as parameters to a certain method, state of a certain class.
  • All above for the nested exception, recursively.

Exception and Message to End User


Since every software application system is ultimately to server our end users, it is beneficial to think exceptions from the end users’ perspective. An exception means something is wrong. From an end user’s perspective, ultimately there are only two kinds of exceptions: user exceptions and system exceptions. A user exception is caused directly by a user with invalid input or incorrect action. End users are interested in this kind of exceptions because, if being informed, they can do something to correct them. For this kind of exceptions, the applications should provide enough information about the exceptions and hints to correct them to the end users, and should provide them the second chance to try. For on-line system, the system does not have to log such exceptions. For batch programs, such exceptions should be written into log files for end users, not for developers and system administrators, to read. System exceptions are not user-related, caused by bug in the application code, corrupted data in database, ill database management systems, ill network communication, or other external systems, etc. In short, they have nothing to do with what the end users have done and the end users can do nothing to correct them. All end users have to know about this kind of exceptions is that they occurred, no more details, particularly, not stack trace. Such exceptions have to be written into log files with as much as possible details for developers and system administrators to investigate. Stack trace of the exceptions should be included in the log files. Eventually, the thread in which a system error occurs should halt, with a brief message to the end user and a detail message in one or more log files for developers and system administrators.
It helps to have two top-level java exceptions for a system:  UserException and SystemException. Every thread started by our systems has to handle, at the top of the calling stack, these two kinds of exceptions if present.

Keep any information that might comprise security of the system out of message to end users.
It helps not to hard code message to end user in programming code. Instead, keep them in certain configuration files, such as Java properties file.

Don’t Create Custom Exception Unnecessarily

If possible, try to use exceptions in stand JDK, well-established libraries or framework, rather than create new custom exception. When a new custom exception is created, it takes time for other people to learn it. It also increases the size of our source code. Doing so only when it brings true benefits that existing exception classes cannot bring.

For more discussion on this issue, please see Item 60: Favor the use of standard exceptions in Chapter 9. Exceptions in Joshua Bloch’s Effective Java, Second Edition.

Sunday, November 11, 2012

Overriding hashCode() in Java May Cause Adverse Effects

One important method of the java.lang.Object class is hashCode(). It is a popular practice to override the hashCode() method in classes that extend the Object class (All Java classes, directly or indirectly extend the Object class). Joshua Block, in his popular book Effective Java, advices Java programmers to "always override hashCode when you override equals" (Item 8, Effective Java, Second Edition). Joshua, however, did not mention the possible adverse effect of overriding the hashCode() method. This article demonstrates such adverse effects in an example.

Our example is a short program that manages student-tutor relationship between students and tutors. Students are represented by objects of the Student class, in Listing 1. Tutors are represented by objects of the Tutor class, in Listing 2. The main program is in Listing 3.

Listing 1


package hashcode.issue;

public class Student {
    public String firstName;
    public String lastName;
    public String phoneNumber;
    
    public Student(String firstName, String lastName, String phoneNumber) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.phoneNumber = phoneNumber;
    }
}

Listing 2

package hashcode.issue;

public class Tutor {
    public String firstName;
    public String lastName;
    public String phoneNumber;
    
    public Tutor(String firstName, String lastName, String phoneNumber) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.phoneNumber = phoneNumber;
    }
}

Listing 3

package hashcode.issue;

import java.util.HashMap;
import java.util.Map;

public class HashCodeIssue {
    private static Map<Student, Tutor"> relationMap = new HashMap<Student, Tutor>();

    public static void main(String[] args) {
        Student john = new Student("John", "Jones", "8945");    // Create a Student object named john
        Tutor tom = new Tutor("Tom", "Petzold", "4627");    // Create a Tutor object named tom
        
        relate(john, tom);    // record the student-tutor relationship between john and tom
        
        // do many other things ...
        
        Tutor johnsTutor = findTutor(john);    // find the tutor of john
        if (johnsTutor != null) {    // print out what we found
            System.out.printf("John's tutor is %s %s\n", johnsTutor.firstName, johnsTutor.lastName);
        } else {
            System.out.println("John does not have a tutor.");
        }
        
        
        // John changed phone number
        john.phoneNumber = "513-326-5489";
        
        johnsTutor = findTutor(john);    // find the tutor of john again
        if (johnsTutor != null) { // print out what we found
            System.out.printf("John's tutor is %s %s\n", johnsTutor.firstName, johnsTutor.lastName);
        } else {
            System.out.println("John does not have a tutor.");
        }

    }
    
    private static void relate(Student student, Tutor tutor) {
        relationMap.put(student, tutor);
    }
    
    private static Tutor findTutor(Student student) {
        return relationMap.get(student);
    }
}

The program maintains the student-tutor relationship by a HashMap, using Student objects as map keys and Tutor objects as map values. A Student object is mutable. One can change a student's first name, last name, and phone number. After we establish a student-tutor relationship between a Student object john and a Tutor object tom by calling the relate() method, we call the findTutor() method to find the tutor of john. We found it. Then we change phone number of john, and call the findTutor() method again to find the tutor. We found the tutor again.

Program execution output:

John's tutor is Tom Petzold 
John's tutor is Tom Petzold 

So far, so good.

Now we override the hashCode() method in the Student class, as in Listing 4 (Line 16 - 19). Then we run the program again. This time, the second call to the findTutor() method failed to find the tutor.

Program execution output:

John's tutor is Tom Petzold 
John does not have a tutor


Listing 4

package hashcode.issue;

import org.apache.commons.lang3.builder.HashCodeBuilder;

public class Student {
    public String firstName;
    public String lastName;
    public String phoneNumber;
    
    public Student(String firstName, String lastName, String phoneNumber) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.phoneNumber = phoneNumber;
    }
    
    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(firstName).append(lastName).append(phoneNumber).build();
    }
}

The failure is due to the fact that when we use a Student object as key in a HashMap, the map calls the hashCode() method on the Student object and uses the returned number to determine hash bucket where the corresponding value resides. In our case, we overrode the hashCode() method to return a  number that depends on value of the student's first name, last number, and phone number. When the student's phone number changes, so does its hash code.

The hashCode() method defined in the java.lang.Object class is different, it always return the same number for the same object. In other words, the returned hash code can be used as the object's identity. When we overrode the hashCode() method to return a number that depends on the object's state (in our example, the Student's first name, last name, and phone number), the hash code can no longer be used as the object's identity.

Conclusion
If an object is mutable and its hashCode() method returns a number depending on its state, do not use the object as hash map key.  In case that you want to use an object as hash map key, ensure at first that it is guaranteed that either the object is immutable or its hashCode() method will always return the same number.

Saturday, March 17, 2012

Executing External Programs via Java Programs Using Plexus Common Utilities

This tutorial shows how for Java programs to execute external programs using the Plexus Common Utilities library. The Plexus Common Utilities library makes it easier for Java programs to execute external programs and shell commands, and open files such as Word documents, PDF documents, etc with appropriate programs, than with the Runtime interface and ProcessBuilder class in the standard Java API.

This JavaWorld articule, When Runtime.exec() won't by Michael C. Daconta discusses how to use the Runtime interface from the standard Java API in the right way to execute external programs and shell commands. This article, Execute an external program, discusses how to use the Runtime interface and ProcessBuilder class from the standard Java API to execute external programs and open PDF files, etc with appropriate programs. Interested readers can read the two articles to appreciate the convenience brought by the Plexus Common Utilities library in regard to executing external programs and shell commands, and opening files with appropriate programs.

In our first example (Listing 1), we execute a maven command from a Java program. The maven command is mvn clean install. It at first cleans a Maven project, build it, and install the artifact built into the local Maven repository. In the Java program, the maven command is specified via Line 18 and 19. The base directory of the Maven project is specified as the working directory of the execution via Line 20. The center of this example is Line 27, where the executeCommandLine() method is called with three arguments. The first argument, commandline is an object of the type org.codehaus.plexus.util.cli.CommandLine. The commandline object captures the command and working directory to execute the external program, plus arguments to the external program. The second and third arguments, out and err, are two object of the type org.codehaus.plexus.util.cli.CommandLineUtils.StringStreamConsumer. The output and error message of the external program go into out and err respectively. The executeCommandLine() method is overloaded. Readers may look up them via the Plexus Common Utilities API.

Listing 1 - Executing an external program

package plexus.examples;

import java.io.File;

import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;


public class ExternalProgramRunner {
    public static void main(String[] args) {
        String command = "D:\\maven\\bin\\mvn.bat";
        String workingDir = "D:\\examples\\plexus-example";
        String[] arguments = new String[]{"clean", "install"};
        
        Commandline commandline = new Commandline();
        commandline.setExecutable(command);
        commandline.addArguments(arguments);
        commandline.setWorkingDirectory(new File(workingDir));
        
        CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
        CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
        
        int exitCode;
        try {
            exitCode = CommandLineUtils.executeCommandLine(commandline, out, err);
        } catch (CommandLineException e) {
            e.printStackTrace();
        }
        
        String output = out.getOutput();
        if (!StringUtils.isEmpty(output)) {
            System.out.println(output);
        }
        
        String error = err.getOutput();
        if (!StringUtils.isEmpty(error)) {
            System.out.println(error);
        }
    }
}


Output of Listing 1 Execution

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Plexus Example 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ plexus-example ---
[INFO] Deleting D:\examples\plexus-example\target
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ plexus-example ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ plexus-example ---
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 2 source files to D:\examples\plexus-example\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ plexus-example ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\examples\plexus-example\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ plexus-example ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.7.1:test (default-test) @ plexus-example ---
[INFO] No tests to run.
[INFO] Surefire report directory: D:\examples\plexus-example\target\surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
There are no tests to run.

Results :

Tests run: 0, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- maven-jar-plugin:2.3.1:jar (default-jar) @ plexus-example ---
[INFO] Building jar: D:\examples\plexus-example\target\plexus-example-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.3.1:install (default-install) @ plexus-example ---
[INFO] Installing D:\examples\plexus-example\target\plexus-example-1.0-SNAPSHOT.jar to C:\users\ted\.m2\repository\ted-tutorial\plexus-example\1.0-SNAPSHOT\plexus-example-1.0-SNAPSHOT.jar
[INFO] Installing D:\examples\plexus-example\pom.xml to C:\users\ted\.m2\repository\ted-tutorial\plexus-example\1.0-SNAPSHOT\plexus-example-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.329s
[INFO] Finished at: Sun Mar 18 23:36:37 EDT 2012
[INFO] Final Memory: 8M/245M
[INFO] ------------------------------------------------------------------------


Listing 2 below shows executing a Windows shell command (i.e. dir) from a Java program (the Java program is executed on a Windows machine). The only differences between Listing 2 and 1 lie on Line 13-15.

Listing 2 - Executing a Shell Command

package plexus.examples;

import java.io.File;

import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;


public class ExternalProgramRunner {
    public static void main(String[] args) {
        String command = "dir";
        String workingDir = "D:\\examples\plexus-example";
        
        Commandline commandline = new Commandline();
        commandline.setExecutable(command);
        commandline.setWorkingDirectory(new File(workingDir));
        
        CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
        CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
        
        int exitCode;
        try {
            exitCode = CommandLineUtils.executeCommandLine(commandline, out, err);
        } catch (CommandLineException e) {
            e.printStackTrace();
        }
        
        String output = out.getOutput();
        if (!StringUtils.isEmpty(output)) {
            System.out.println(output);
        }
        
        String error = err.getOutput();
        if (!StringUtils.isEmpty(error)) {
            System.out.println(error);
        }
    }
}

Output of Listing 2 Execution

Directory of D:\\examples\plexus-example

03/18/2012 11:36 PM <DIR> .
03/18/2012 11:36 PM <DIR> ..
03/12/2012 05:34 PM 421 .classpath
03/12/2012 05:34 PM 506 .project
03/12/2012 05:34 PM <DIR> .settings
03/12/2012 05:33 PM 1,330 pom.xml
03/12/2012 05:12 PM <DIR> src
03/18/2012 11:36 PM <DIR> target
3 File(s) 2,257 bytes
5 Dir(s) 554,998,767,616 bytes free

Listing 3 below shows opening a PDF file with an appropriate program (i.e. Adobe Reader) from a Java program. In this example, a PDF file named plexus-guide.pdf is located under D:\docs. Note that nowhere is ever Adobe Reader is mentioned, nevertheless the installation path, in the Java program.Plexus Common Utilities just figures it out behind the scene. The only differences between Listing 3 and 1 lie on Line 13-15.

Listing 3 - Opening a PDF File with Adobe Reader

package plexus.examples;

import java.io.File;

import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;


public class ExternalProgramRunner {
    public static void main(String[] args) {
        String command = "plexus-guide.pdf";
        String workingDir = "D:\\docs";
        
        Commandline commandline = new Commandline();
        commandline.setExecutable(command);
        commandline.setWorkingDirectory(new File(workingDir));
        
        CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
        CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
        
        int exitCode;
        try {
            exitCode = CommandLineUtils.executeCommandLine(commandline, out, err);
        } catch (CommandLineException e) {
            e.printStackTrace();
        }
        
        String output = out.getOutput();
        if (!StringUtils.isEmpty(output)) {
            System.out.println(output);
        }
        
        String error = err.getOutput();
        if (!StringUtils.isEmpty(error)) {
            System.out.println(error);
        }
    }
}

Sunday, January 1, 2012

Type Wildcard in Java Generics - A Tutorial

Overview

Type wildcard is the most tricky part of Java Generics. The readers are assumed having the basic knowledge of Java Generics. For a quick review of the basics of Java Generics, see my other post, The Basics of Java Generics. Type wildcard is only used in parameterized types, as the type arguments. A type wildcard is never used in a definition of generic type or generic method . In practice, such parameterized types are commonly used in method definitions, serving as the type of the formal parameters of the methods.

A parameterized type may take one of four forms:
  1. List<String>, here String is a concrete type, serving as the type argument for the parameterized type List<String>
  2. List<?>, where ? is a type wildcard without a bound, serving as the type argument for the parameterized type List<?> 
  3. List<? extends Number>, where ? is a type wildcard with a upper bound, serving as the type argument for the parameterized type List<? extends Number>, and Number is the upper bound of the wildcard.
  4. List<? supper Number>, where ? is a type wildcard with a lower bound, serving as the type argument for the parameterized type List<? super Number>, and Number is the lower bound of the wildcard.
For a parameterized type with a type wildcard as a type argument, the wildcard may have either a single upper or lower bound, but not both.

In a World without Type Wildcard

To understand why Java introduces the type wildcard for parameterized type, let’s look at an example. This example is about a circus. In the circus, there are two kinds of animals, birds and dogs. Brids can fly. Dogs can bark. A special kind of birds, nightingares, can also sing. Class hierarchy of animals used in this example are shown in Figure 1. Code for all kinds of animals are shown in Listing 1-1 to 1-4.

Figure 1 - Animal Hierarchy


Listing 1-1 - Animal
package animals;

public class Animal {
    private String name;
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void jump() {
        System.out.println(getName() + " is jumping.");
    }
    
    public String getName() {
        return name;
    }
}

Listing 1-2 -Brid
package animals;

public class Bird extends Animal {    
    public Bird(String name) {
        super(name);
    }
    
    public void fly() {
        System.out.println(getName() + " is flying.");
    }
}

Listing 1-3 -  Nightingale
package animals;

public class Nightingale extends Bird {
    public Nightingale(String name) {
        super(name);
    }
    
    public void sing() {
        System.out.println(getName() + " is singing.");
    }
}

Listing 1-4 - Dog
package animals;

public class Dog extends Animal {   
    public Dog(String name) {
        super(name);
    }
    
    public void bark() {
        System.out.println(getName() + " is barking.");
    }
}

There is a kind of actors in the circus called AnimalTrainer. When given a collection of animals, the AnimalTrainer commands them to jump. The first version of AnimalTrainer is shown in Listing 2-1.

Listing 2-1 - AnimalTrainer Version 1 - Without Type Wildcard
package nowildcard;
import java.util.List;
import animals.Animal;

public class AnimalTrainer {
    public void act(List<Animal> animalList) {
        for (Animal animal : animalList) {
            animal.jump();
        }
    }
}

The first version of the main class, Circus, is shown in Listing 2-2. When the Circus program is launched, it will at first create an AnimalTrainer. Then a list of Animals is created, and two animals are added into the list. It finally invokes the act method, passing the animal list, on the AnimalTrainer. The AnimalTrainer acts by command every animal to jump. So far so good.

Listing 2-2 - Circus Version 1
package nowildcard;
import java.util.ArrayList;
import java.util.List;
import animals.Animal;
import animals.Bird;
import animals.Dog;

public class Circus {
    public static void main(String[] args) {
        AnimalTrainer animalTrainer = new AnimalTrainer();
        
        List<Animal> animalList = new ArrayList<Animal>();
        animalList.add(new Dog("Bob"));
        animalList.add(new Dog("Amy"));
                
        animalTrainer.act(animalList);
    }
}

The second version of the main class, Circus, is shown in Listing 2-3. The AnimalTrainer commands a list of birds to jump. We reason that it will be fine since a bird is actually an animal and can jump. This version of the Circus class, however, does not compile. The line with trouble is  animalTrainer.act(birdList); (Line 15) The error message is:
The method act(List<Animal>) in the type AnimalTrainer is not applicable for the arguments (List<Bird>)


Listing 2-3 - Circus Version 2
package nowildcard;
import java.util.ArrayList;
import java.util.List;

import animals.Bird;

public class Circus {
    public static void main(String[] args) {
        AnimalTrainer animalTrainer = new AnimalTrainer();
        
        List<Bird> birdList = new ArrayList<Bird>();
        birdList.add(new Bird("Tim"));
        birdList.add(new Bird("Nancy"));

        animalTrainer.act(birdList);
    }
}

In fact, Java does not regard List<Bird> as a subtype of List<Animal>. So the act method only accepts List<Animal> as argument, rejecting List<Bird>. You might wonder why Java has such an “unreasonable” rule. Actually, this rule exists for a very good reason. Let’s see why.


What if List<Bird> Were Regarded as a Subtype of List<Animal>?

If Java regarded List<Bird> as a subtype of List<Animal>, we were going to have trouble. To show the trouble, let’s update the Circus class as in Listing 3-3. At first lets look at the two new classes newly added, Magician as in Listing 3-1, and BirdTrainer as in Listing 3-2. The act method of Magician takes a list of Animals, and replaces all Animals in the list by two Dogs. The Magician class compiles perfectly. The act method of BirdTrainer takes a list of Birds and commands each of them to fly. The BirdTrainer class as well compiles perfectly. The Circus class (Version 3, Listing 3-3) does not compile for the same reason as in the Version 2 (Listing 2-3). Line 16 fails. The error message is:
The method act(List<Animal>) in the type Magician is not applicable for the arguments (List<Bird>)  

The Java compiler rejects Line 16 for a good reason. If Java regarded List<Bird> as a subtype of List<Animal> and allowed Line 16 compile, we were going to have run time exception because at run time, when the BirdTrainer got a list, the list actually would contain two Dogs. The Dogs would be commanded to fly but they cannot (a Dog object does not have a fly method). In other words, the Java type integrity would be broken.

To prevent trouble of this nature, Java does not regard List<Bird> as a subtype of List<Animal>. In a generic term of generics, it is said that the type parameter T in List<T> is non-variant.

Listing 3-1 - Magician Version 1

package whatif;
import java.util.List;

import animals.Animal;
import animals.Dog;

public class Magician {
    public void act(List<Animal> animalList) {
        for (int i = 0; i < animalList.size(); i++) {
            animalList.remove(i);
        }
        
        animalList.add(new Dog("Green"));
        animalList.add(new Dog("Red"));        
    }
}

Listing 3-2 - BirdTrainer Version 1

package whatif;
import java.util.List;

import animals.Bird;

public class BirdTrainer {
    public void act(List<Bird> birdList) {
        for (Bird bird : birdList) {
            bird.fly();
        }
    }
}

Listing 3-3 - Circus Version 3

package whatif;
import java.util.ArrayList;
import java.util.List;

import animals.Bird;

public class Circus {
    public static void main(String[] args) {
        BirdTrainer birdTrainer = new BirdTrainer();
        Magician magician = new Magician();
        
        List<Bird> birdList = new ArrayList<Bird>();
        birdList.add(new Bird("Tim"));
        birdList.add(new Bird("Nancy"));
        
        magician.act(birdList);    // this line won't compile.
        
        birdTrainer.act(birdList);
    }
}
 
Type Wildcard with a Upper Bound

Now since we understand why List<Bird> is not regarded as a subtype of List<Animal>, we can come back to seek a solution for our situation: How to enable an AnimalTrainer to accept a List<Bird> as well as a List<Animal>. Java’s solution is type wildcard. Specifically, we can change the AnimalTrainer class to be like in Listing 4-1 (Line 7: act(List<? extends Animal> animalList)). The ? in Line 7 is called a type wildcard, and ? extends Animal says Animal is the upper bound of the type wildcard.
With this version of AnimalTrainer, the main class, Circus, as in Listing 4-2 (it is essentially the same as in Listing 2-3) compiles and runs successfully.

Listing 4-1 - AnimalTrainer Version 2, With Type Wildcard With Upper Bound

package upperbound;
import java.util.List;

import animals.Animal;

public class AnimalTrainer {
    public void act(List<? extends Animal> animalList) {
        for (Animal animal : animalList) {
            animal.jump();
        }
    }
}

Listing 4-2 - Circus Version 4

package upperbound;
import java.util.ArrayList;
import java.util.List;

import animals.Bird;

public class Circus {
    public static void main(String[] args) {
        AnimalTrainer animalTrainer = new AnimalTrainer();
        
        List<Bird> birdList = new ArrayList<Bird>();
        birdList.add(new Bird("Tim"));
        birdList.add(new Bird("Nancy"));
        
        animalTrainer.act(birdList);
    }
}

In general, there are three rules about type wildcard with a upper bound, given a generic type G (e.g. List<E>), and two concrete types X and Y where Y is a subtype of X (e.g. Bird is a subtype of Animal):
  1. G<? extends Y> is subtype of G<? extends X> (e.g. List<? extends Bird> is a subtype of List<? extends Animal>
  2. G<X> is subtype of G<? extends X> (e.g. List<Bird> is a subtype of List<? extends Bird>)
  3. G<?> is just a shorthand for G<? extends Object> (e.g. List<?> is a shorthand for List<? extends Object>)
(For the formal specification, see 4.5.1.1 Type Argument Containment and Equivalence, The Java Language Specification, Third Edition)



(Those rules have something to do with the idea of covariance in the theory of generics. They however do not align very well with the theory.)


In the context of our example, Bird is a subtype of Animal, so List<? extends Bird> is a subtype of List<? extends Animal>. Meanwhile, List<Bird> is a subtype of List<? extends Bird>. Therefore List<Bird> is a subtype of List<? extends Animal>. That 

is the reason why the act method has a formal parameter of the type List<? extends Animal> and can accept List<Bird> and List<Animal> as argument.

You might wonder whether the Magician class can also be updated with type wildcard and bring back the trouble of messing up birds by dogs. It cannot. The catch is that when the act method of the Magician is updated to take a parameter of the type List<? extends Animal>, as in Listing 4-3 (Line 8), it is no longer allowed to call the add method of the List class. The compile error message is "The method add(capture#3-of ? extends Animal) in the type List<capture#3-of ? extends Animal> is not applicable for the arguments (Dog)". Therefore, it cannot add dogs into the list.  It fails because the add method here expects an argument of the type “? extends Animal” but Dog is not a subtype of it (Note: even though List<Dog> is a subtype of List<? extends Animal>, Dog is not a subtype of "? Extends Animal". More generally, no class is regard as a subtype of “? Extends Animal”.  It is said that “? Extends Animal” is an undefined type (Note: meanwhile, List<? Extends Animal> is a
well defined parameterized type.)

Listing 4-3 Magician Version 2


package upperbound;
import java.util.List;

import animals.Animal;
import animals.Dog;

public class Magician {
    public void act(List<? extends Animal> animalList) {
        for (int i = 0; i < animalList.size(); i++) {
            animalList.remove(i);
        }
        
        animalList.add(new Dog("Green")); // now these two lines do not compile
        animalList.add(new Dog("Red"));    
    }
}

Based on my experience, the general rule is like this: When a method with a parameter of a parameterized type that is one with type wildcard with a upper bound (e.g. act(List<? extends Animal>)), inside the body of the method, it is not allowed to call any method on the parameter object unless the method is parameterless (e.g. add(E element) on List<E>). I tried to find something about it in the Java Language Specification but found nothing. However, it seems that all Java compilers that I saw are implemented in this way. I could not figure out how to derive this rule from other more basic rules in the Java Specification.

Type Wildcard with a Lower Bound

Similar to type wildcard with a upper bound, a type wildcard may have a lower bound. (However, a type wildcard cannot have both upper and lower bound, nor have more than one upper or lower bounds). The syntax is like this: List<? super Bird> where Bird is the lower bound of the type wildcard.

In general, there are also three rules about type wildcard with a lower bound, given a generic type G (e.g. List<E>), and two concrete types X and Y where X is a subtype of Y (e.g. Bird is a subtype of Animal):
  1. G<? super Y> is a subtype of G<? super X> (e.g. List<? super Animal> is a subtype of List<? super Bird>
  2. G<X> is subtype of G<? super X> (e.g. List<Bird> is a subtype of List<? super Bird>)
(Again, for the formal specification, see 4.5.1.1 Type Argument Containment and Equivalence, The Java Language Specification, Third Edition)

(Those rules have something to do with the idea of contra-variance in the theory of generics. They however do not align very well with the theory.)

Let's illustrate the uses of type wildcard with a lower bound with another version of our Circus program as in Listing 5-1. In this version of the Circus class, an animalList (of type List<Animal>) and a birdList (of type List<Bird>) are created in the main method. Instead of adding animals or birds to the lists in the main method, the animalList is passed to the addBirds method of a BirdKeeper object, and the addDogs method of a DogKeeper object, to add birds and dogs into it. Then an AnimalTrainer acts on the animalList by commanding the animals (birds and dogs) in the list to jump. Also, the birdList is passed to the addBirds method of the BirdKeeper object to add birds to it.  And a BirdTrainer acts on the list by commanding the birds in the birdList to fly. The pertinent version of AnimalTrainer and BirdTrainer are shown in Listing 5-2 and 5-3.

Notice the following points in the above example. For the animalList to contain both birds and dogs, it must be defined as of type List<Animal>, instead of List<Bird> or List<Dog>. For the BirdTrainer to command birds in the birdList to fly, the birdList must be defined as of type List<Bird>, instead of List<Animal>.

For the program to work, the addBirds method of the BirdKeeper class must be able to:
  1. accept a List<Animal> as argument (Line 21, the Circus class)
  2. accept a List<Bird> as argument (Line 26, the Circus class)
  3. add birds into the List<Animal> and the List<Bird> (Line 7-8, the BirdKeeper class)
The solution is shown in Listing 5-4. The type of the formal parameter to the addBirds method is List<? super Bird>. The parameterized type that servers as the type of the formal parameter is a type wildcard with a lower bound. Other choices won't work. If we had the method header as public void addBirds(List<Animal> animalList), the method would not accept List<Bird> as argument; if we If we had the method header as public void addBirds(List<Bird> animalList), the method would not accept List<Animal> as argument; If we had the method header as public void addBirds(List<? extends Animal> animalList), the method would not be able to add anything to the list (i.e. animalList). A type wildcard with a lower bound is the necessary solution to this case.  

Notice that it is allowed to call a method with parameters (e.g the add method of List<E>) on an object referenced by a formal parameter of type wildcard with a lower bound (e.g. the parameter named animalList in the addBirds method of the BirdKeeper class), while it is not allowed if the wildcard has a upper bound.

Similarly, the DogKeeper class is shown in Listing 5-5.

Listing 5-1 - Circus Version 5

package lowerbound;
import java.util.ArrayList;
import java.util.List;

import animals.Animal;
import animals.Bird;

public class Circus {
    public static void main(String[] args) {
        AnimalTrainer animalTrainer = new AnimalTrainer();
        BirdTrainer birdTrainer = new BirdTrainer();
        
        List<Animal> animalList = new ArrayList<Animal>();
        
        BirdKeeper birdKeeper = new BirdKeeper();
        birdKeeper.addBirds(animalList);
               
        DogKeeper dogKeeper = new DogKeeper();        
        dogKeeper.addDogs(animalList);
                
        animalTrainer.act(animalList);
        
        List<Bird> birdList = new ArrayList<Bird>();
        birdKeeper.addBirds(birdList);
        
        birdTrainer.act(birdList);
    }
}

Listing 5-2 - AnimalTrainer Version 3

package lowerbound;
import java.util.List;
import animals.Animal;

public class AnimalTrainer {
    public void act(List<Animal> animalList) {
        for (Animal animal : animalList) {
            animal.jump();
        }
    }
}

Listing 5-3 - BirdTrainer Version 2

package lowerbound;
import java.util.List;
import animals.Bird;

public class BirdTrainer {
    public void act(List<Bird> birdList) {
        for (Bird bird : birdList) {
            bird.fly();
        }
    }
}


Listing 5-4 - BirdKeeper
package lowerbound;
import java.util.List;
import animals.Bird;

public class BirdKeeper {
    public void addBirds(List<? super Bird> animalList) {
        animalList.add(new Bird("Tim"));
        animalList.add(new Bird("Nancy"));
    }
}

Listing 5-5 - DogKeeper

package lowerbound;
import java.util.List;
import animals.Dog;

public class DogKeeper {
    public void addDogs(List<? super Dog> animalList) {
        animalList.add(new Dog("Bob"));
        animalList.add(new Dog("Amy"));
    }
}

Finally, notice that the addBirds method of the BirdKeeper class won't accept an argument of the type List<Nightingale> because List<Nightingale> is not regarded as a subtype of List<? super Bird>. The version of the Circus class as shown in Listing 5-5 won't compile. Line 30, birdKeeper.addBirds(nightingaleList);, will fail. The compile error is:
The method addBirds(List<? super Bird>) in the type BirdKeeper is not applicable for the arguments (List<nightingale>)

Listing 5-5 - Circus Version 6

package lowerbound;
import java.util.ArrayList;
import java.util.List;
import animals.Animal;
import animals.Bird;
import animals.Nightingale;

public class Circus {
    public static void main(String[] args) {
        AnimalTrainer animalTrainer = new AnimalTrainer();
        BirdTrainer birdTrainer = new BirdTrainer();
        
        List<Animal> animalList = new ArrayList<Animal>();
        
        BirdKeeper birdKeeper = new BirdKeeper();
        birdKeeper.addBirds(animalList);
        
        
        DogKeeper dogKeeper = new DogKeeper();        
        dogKeeper.addDogs(animalList);
                
        animalTrainer.act(animalList);
        
        List<Bird> birdList = new ArrayList<Bird>();
        birdKeeper.addBirds(birdList);
        
        birdTrainer.act(birdList);
        
        List<Nightingale> nightingaleList = new ArrayList<Nightingale>();
        birdKeeper.addBirds(nightingaleList); // does not compile
    }
}

Conclusion
  1. Type wildcard is used to increase flexibility of methods that take parameters of parameterized types
  2. It is not allowed to call a method on an object referenced by a parameter of a parameterized type with type wildcard with a upper bound unless the method is parameter less.
  3. It is  allowed to call any method on an object referenced by a parameter of a parameterized type with type wildcard with a lower bound.
References 
  • The Java Language Specification, Third Edition, by James Gosling, Bill Joy, Guy Steele, and Gilad Bracha, Addison Wesley Professional, 2005
  • The Java Programming Language, 4th Edition, by Ken Arnold, James Gosling, and David Holmes, Prentice Hall PTR, 2005

Tuesday, November 29, 2011

The Basics of Java Generics

Overview

For the sake of generics, Java types (classes and interfaces) can be grouped into three categories:
  • Ordinary type, e.g. String, Integer
  • Generic type, e.g. java.lang.Comparable<T>, java.util.List<E> , and java.util.ArrayList<E>
  • Parameterized type, e.g. java.lang.Comparable<Integer>, java.util.List<String>, and java.util.ArrayList<String>
In Java, there are four kinds of generic constructs:
  • generic interface
  • generic class
  • generic method
  • generic constructor
Constructors are very much like methods, except that there is not any return for constructors. For this reason, we are going to omit any discussion about generic constructors since all discussions about generic methods, except what about method returns, also apply to generic constructors.

Coding with generics usually involves one or more of the following:
  • Defining a generic interface, class, or method
  • Invoking a generic interface, or class
  • Invoking a generic method
  • Defining a non-generic method with at least a parameter of a generic type, or with the return of a generic type. Such a method must be a member of a generic type
  • Defining a method with at least a parameter of a parameterized type, or with the return of a parameterized type
  • Invoking a method with at least a parameter of a parameterized type
  • Invoking a method with the return of a parameterized type

Defining Generic Types

Example 1 – Defining a generic interface

public interface Iterable<T>

The T in Iterable<T> is called a type parameter. In the language of Java generics, we say that the generic type Iterable takes a type parameter, T. Conventionally, a single upper case T is used as identifier for a type parameter (T stands for type).

Example 2 – Defining a generic interface that extends another generic interface

public interface List<E> extends Collection<E>

Here the E in List<E> is the type parameter. Conventionally, E is used as identifier for type parameter of collections. (E stands for element)

Example 3 – Defining a generic class that implements a generic interface

public class ArrayList<E> implements List<E>


Defining Generic Method

Example  4 – Defining a generic method

<T> T[] toArray(T[] a);

Above is the definition of a toArray method in the body of java.util.List. The first <T> tells that this is a generic method and the method takes a type parameter T. This means that this method has a hole that will be filled later with a concrete type. Then it also tells that the type of the method return is T[],  and the type of the method parameter is a T[] (array of T). Essentially, the type parameter of this method establishes a constrain, in term of type, between the method return and parameters. If we want to turn a list into a String [], we must pass to the toArray method a String[].

Please note that a method whose parameters or return is of a type parameter is not necessarily a generic method unless in its definition  <T>  is placed before its return type (or void).  For example, the methods shown in Example 5 below are not generic methods.

On the other hand, even a non-generic type may have a generic method as its member.


Type Parameter

A type parameter is a placeholder for a concrete type.  It is important to understand that a type parameter is either taken by a generic type,  a generic method, or a generic constructor. On the other hand a generic type or method takes at least one type parameter.

Inside the body of a generic interface or class, a type parameter taken by the interface or class, can server as the type of parameters, the type of return, or the type of local variables, of an instance method. It can also server as the type of instance fields.

A type parameter taken by a generic method can server as type of its parameters, type of its return, or type of its local variables. (Note: It is legal for a generic method to be a static member of a class or interface)

A type parameter taken by a class or interface cannot be:
  • Type of its static fields (because there is only one class vs. many different T)
  • Anywhere in its static member methods (same reason)
  • In a static initial block (same reason)

In addition, none type parameter can be
  • used in new T() statement  to create a new object (because erasure)
  • used in new T[size]() to create a new array of objects (because erasure)

Example 5 - Defining methods with parameters or return of type parameter

boolean add(E e);
E get(int index);

The above two methods are defined in the body of generic List<E>, the type parameter E servers as the type of parameter named e for the method named add, and the return type of the method named get. These two methods are not generic methods. The type parameter E is not taken by the methods but by their owner type (i.e. List<E>).


Generic Type v.s.  Parameterized Type

It is critical to understand the difference and relationship between generic type and parameterized type. For example, ArrayList<E> is a generic type and  ArrayList<String> is a parameterized type.  They differ in the following aspects:
  • E in  ArrayList<E>  is a type parameter, and String  in  ArrayList<String> is an concrete type (particularly, a ordinary class). In regard to  ArrayList<String>, the concrete type String servers as the type argument, to fill the place held by type parameter E, which is taken by  ArrayList<E>.
  • A parameterized type, like an ordinary type, is a concrete type, while a generic type is an abstract type
  • It is legal to create an object of ArrayList<String> via statement new ArrayList<String>();, statement new ArrayList<E>(); is, however, illegal.
  • More generally, the usage of a parameterized type is exactly the same as an ordinary type. A parameterized type can be used at any place where an ordinary type is to be used, i.e. to be used as the type of a variable or a method return. The variable may be a method parameter, a local variable, or a field.
A parameterized type always has a special relationship with a generic type: a parameterized type is always instantiated out of a generic type. For example, ArrayList<String> is instantiated out of ArrayList<E>, by replacing a type argument, String, for the type parameter, E. In order for ArrayList<String> to exist, ArrayList<E> must exist first.

A type parameter is like a hole. When the hole is filled with a concrete type, a parameterized type comes out of the generic type. Replacing a type parameter by a concrete type is called invocation of a generic type. While one can invoke a method passing arguments, one can invoke a generic type passing type arguments.

In a parameterized type a type argument takes all places used to be held by its corresponding type parameter.  For example, in List<String>, there are effectively
boolean add(String e);
String get(int index);

(For the formal specification, see 4.5.2 Members and Constructors of Parameterized Types, The Java Language Specification, Third Edition, Addision Wesley, 2004)


Bounded Type Parameter

In a generic type definition, a type parameter may be given an upper bound.


Example 6 – Defining a generic type named SortedSet which is a set with elements sorted
 

public interface SortedSet<E extends Comparable<E>> extends Set<E>
 

Here <E extends Comparable<E>> indicates that E is a bounded type parameter and Comparable<E> is the upper bound. Any parameterized type out of this generic type must have the type argument as a sub-type of Comparable<E>.  For example, we may have a parameterized type SortedSet<Integer>. It is OK since Integer implements Comparable<Integer>. However, we cannot have a parameterized type SortedSet<java.io.File> because File does not implement Comparable<File>. In short, the bound of a type parameter is used to restrict the type arguments to the generic type. Without a bound, any type will be accepted as legal type argument at compile time. Some of them may lead to runtime exception.
 

A few more words about the example, the upper bound, java.lang.Comparable<E>,  is also a generic type, and its type parameter is E, the same as of SortedSet.

If there are multiple such bounds, separate them by & in the generic type definition.




Calling a Generic Method


Example 7 - Calling a generic method


         List<String> list = new ArrayList<String>();
         list.add("One");
         list.add("Two");
       
         String[] stringArray = list.toArray(new String[]{});
       
         System.out.println(stringArray[0]);
         System.out.println(stringArray[1]);

Usually, it is not required to specifying the type argument (i.e. the concrete type to take the place of the type parameter) when call a generic method, as the example above shows, because the compiler can infer the type argument from the type of the argument to the method (i.e. String[] in the example). That is however, not always the case. In some cases, the compiler cannot determine the concrete type by inference. Then the type argument has to be explicitly specified. The right syntax to specify the type argument to a generic method is show in the example below:


String[] stringArray = list.<String>toArray(new String[]{});
 

The type argument (e.g. String in the example above) is place between < and >, and immediately before the name of the generic method being called.

By the way, be aware that the toArray method does not bring complete type-safety. The following code fragment compiles but causes run time exception.


        List<String> list = new ArrayList<String>();
        list.add("One");
        list.add("Two");
        

        Integer[] intArray = list.toArray(new Integer[]{});

Type Wildcard

Type wildcard in Java Generics is a complex topic. It is discussed in my other post Type Wildcard in Java Generics.