Section 9.3. Function


9.3. Function

The Function pattern converts an arbitrary method into an object. The relation between a function and the corresponding method is similar to the relation between Comparator and the compareTo method.

The generic version of the Function pattern demonstrates how to use a type variable in the tHRows clause of a method declaration. This may be useful when different instances of a class contain methods that may raise different checked exceptions.

Recall that the class THRowable has two major subclasses, Exception and Error, and that the first of these has another major subclass, RuntimeException. An exception is checked if it is a subclass of Exception but not a subclass of RuntimeException. The throws clause of a method may list any subclass of THRowable, but must list any checked exception that might be thrown by the method body, including any checked exceptions declared for the methods invoked within the body.

An example of the use of a type variable in a tHRows clause is shown in Example 9.5. The example defines a class, Function<A, B, X>, which represents a function. The class contains an abstract method, apply, that accepts an argument of type A, returns a result of type B, and may throw an exception of type X. The class also contains an applyAll method that accepts an argument of type List<A>, returns a result of type List<B>, and again may throw an exception of type X; the method invokes the apply method on each element of the argument list to produce the result list.

Type parameter in a throws clause

 import java.util.*; import java.lang.reflect.*; interface Function<A, B, X extends Throwable> {   public B apply(A x) throws X; } class Functions {   public <A, B, X extends Throwable>   List<B> applyAll(List<A> list) throws X {     List<B> result = new ArrayList<B>(list.size());     for (A x : list) result.add(apply(x));     return result;   }   public static void main(String[] args) {     Function<String, Integer, Error> length =       new Function<String, Integer, Error>() {         public Integer apply(String s) {           return s.length();         }       };     Function<String, Class<?>, ClassNotFoundException> forName =       new Function<String, Class<?>, ClassNotFoundException>() {         public Class<?> apply(String s)           throws ClassNotFoundException {           return Class.forName(s);         }       };     Function<String, Method, Exception> getRunMethod =       new Function<String, Method, Exception>() {         public Method apply(String s)           throws ClassNotFoundException,NoSuchMethodException {           return Class.forName(s).getMethod("run");         }       };     List<String> strings = Arrays.asList(args);     System.out.println(applyAll(length, strings));     try { System.out.println(applyAll(forName, strings)); }     catch (ClassNotFoundException e) { System.out.println(e); }     try { System.out.println(applyAll(getRunMethod, strings)); }     catch (ClassNotFoundException e) { System.out.println(e); }     catch (NoSuchMethodException e) { System.out.println(e); }     catch (RuntimeException e) { throw e; }     catch (Exception e) { throw new AssertionError(); }   } } 

The main method of the class defines three objects of this type. The first is length of type Function<String, Integer, Error>. It accepts a string and returns an integer, which is the length of the given string. Since it raises no checked exceptions, the third type is set to Error. (Setting it to RuntimeException would work as well.)

The second is forName of type Function<String, Class<?>,ClassNotFoundException>. It accepts a string and returns a class, namely the class named by the given string. The apply method may throw a ClassNotFoundException, so this is taken as the third type parameter.

The third is geTRunMethod of type Function<String, Method, Exception>. It accepts a string and returns a method, namely the method named run in the class named by the given string. The body of the method might raise either a ClassNotFoundException or a NoSuchMethodException, so the third type parameter is taken to be Exception, the smallest class that contains both of these exceptions.

This last example shows the chief limitation of giving generic types to exceptions. Often there is no suitable class or interface that contains all exceptions the function may raise, and so you are forced to fall back on using Exception, which is too general to provide useful information.

The main method uses applyAll to apply each of the three functions to a list of strings. Each of the three invocations is wrapped in a try statement appropriate to the exceptions it may throw. The length function has no TRy statement, because it throws no checked exceptions. The forName function has a try statement with a catch clause for ClassNotFoundException, the one kind of exception it may throw. The geTRunMethod function requires a TRy statement with catch clauses for ClassNotFoundException and NoSuchMethodException, the two kinds of exception it may throw. But the function is declared to throw type Exception, so we need two additional "catchall" clauses, one to rethrow any run-time exception that is raised, and one to assert that it is an error if any exception is raised that is not handled by the previous three clauses. For this particular example, re-raising runtime exceptions is not required, but it is good practice if there may be other code that handles such exceptions.

For example, here is a typical run of the code, printing the list of lengths, the list of classes, and the list of methods (the last list has been reformatted for readability, since it doesn't fit on one line):

 %  java Functions java.lang.Thread java.lang.Runnable [16, 18] [class java.lang.Thread, interface java.lang.Runnable] [public void java.lang.Thread.run(),  public abstract void java.lang.Runnable.run()] 

And here is a run that raises NoSuchMethodException, since java.util.List has no run method:

 % java Functions java.lang.Thread java.util.List [16, 14] [class java.lang.Thread, interface java.util.List] java.lang.NoSuchMethodException: java.util.List.run() 

And here is a run that raises ClassNotFoundException, since there is no class named Fred:

 % java Functions java.lang.Thread Fred [16, 4] java.lang.ClassNotFoundException: Fred java.lang.ClassNotFoundException: Fred 

The exception is raised twice, once when applying forName and once when applying getrunMethod.




Java Generics and Collections
Java Generics and Collections
ISBN: 0596527756
EAN: 2147483647
Year: 2006
Pages: 136

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net