COMP6700: Lectures Block 3

Alexei B Khorev

March 2013

Topics of the block 3

In this part we shall consider The Java language galore

  1. Java’s I/O — Streams

    • Java 7’s New I/O
  2. StringTokenizer

  3. Scanner

  4. Exceptions

  5. Assertions

  6. Packages

  7. Enumerations

  8. Generic (parameterised) types

  9. Reflections (time permits)

Getting input into program: I/O packages

Two previously discussed ways to provide input to a program:

are very limited in what input can be provided, and how (un)flexibly it can be used. They are too primitive if we need:

The Java APIs which allow you to do this are the so-called I/O packages:

I/O streams

From Java Tutorial

An I/O Stream represents an input source or an output destination. A stream can represent many different kinds of sources and destinations, including disk files, devices, other programs, and memory arrays.

Streams support many different kinds of data, including simple bytes, primitive data types, localised characters, and objects. Some streams simply pass on data; others manipulate and transform the data in useful ways. No matter how they work internally, all streams present the same simple model to programs that use them. A stream is a sequence of data.

Input Output
A program uses an input stream to to read data from a source, one item at a time A program uses an output stream to to write data to a destination, one item at a time
input_stream output_stream

java.io package

The java.io defines I/O in terms of streams. Streams are ordered sequences of data that have a source (input streams) or destination (output streams). The I/O classes act as front ends to the specific details of OS, providing access to the system resources through files, peripheral devices (keyboard, display screen) and other means. Operations which can be carried out over the streams are provided by in interfaces and abstract classes. Concrete classes (eg Filters) may have additional methods.

Stream are objects of corresponding I/O classes. Depending on the processing task, methods of the stream class are called, or the object is used as a parameter for a constructor of another stream, eg, for filtering or buffering the original stream (see two examples, ByteCounter.java and UppercaseConvertor.java):

// wrapping std input as a character stream
InputStreamReader cin = new InputStreamReader(System.in); 
java ByteCounter; // run demo
java UppercaseConvertor // run demo

Input to and output from a program

Learning how to read() and write()

decorating_streams

I/O Stream Classes

Methods of Stream classes

  • read() — reads a character from the stream returns int, -1 means end of stream
  • write() — writes a character/string to the stream
  • readLine() (in BufferedReader) — read a line of text
  • newLine() (in BufferedWriter) — writes a line separator
  • flush() (in BufferedWriter) — clears the buffer by writing it to the destination stream
  • print(), println() (in PrintWriter) — convert the value of data (primitives and objects) into a printable form and send them to the destination stream

byte_stream

Formatted Input Stream

Problem: Process a formatted input — read an input text stream, organised line-by-line as a series of fields by breaking the lines and using each field accordingly.

The StringTokenizer class from java.util package is useful for processing formatted input. It breaks the input (when reading from a file line-by-line) into substrings which can be processed according to specified format. Eg, data from a file with astronomical records (fields) on every line including:

  • starName
  • catNum
  • class
  • coor1,coor2,coor3
  • luminosity

are read, parsed and then star objects created and added to a catalog.

tokenizer

String Tokenizer

This is how the program may look (hint: relevant to one of Assignment 1 tasks):

ArrayList<Star> stars; // a star catalog                  
BufferedReader input = new BufferedReader(new FileReader(catalog.txt));            
StringTokenizer tokens;                                   
String starName;                                          
int catalogNumber;                                        
char starClass;                                           
Coordinates coor;                                         
double lum;                                               
String line = input.readLine();                           
while (line != null) {                                    
    tokens = new StringTokenizer(line);                   
    starName = tokens.nextToken();                        
    catalogNumber = Integer.parseInt(tokens.nextToken()); 
    starClass = tokens.nextToken().charAt(0);             
    coor = new Coordinates(tokens.nextToken());           
    lum = Double.parseDouble(tokens.nextTokens());        
    star = new Star(<all the collected values>);          
    stars.add(star);                                      
    line = input.readLine();                              
}                                                         

Alternative to Tokenizer

StringTokenizer is a legacy class that is kept for backward compatibility, but its use is discouraged in new code. A better way to exercise the same functionality by using the split() method of String.

// "\\s" is a regex for any number of WS characters
String[] result = "this is a test".split("\\s"); 
for (String str: result)
    System.out.println(str + " has " + str.length());
output:
this has 4
is has 2
a has 1
test has 4

You are free to choose which method to use — StringTokenizer, or String.split(). But the trend is to weed out the former one. Apart from StringTokenizer, java.io package has StreamTokenizer, which can recognise identifiers, numbers, quoted strings, and various comment styles. It is specially designed to process the text code for languages like Java and C.

Scanner : the Great Simplifier

Scanner class combines the facilities of InputStreams, StringTokenizer and Regex classes to break down formatted input into tokens and translating individual tokens according to their data type. Scanner is not a stream (more like a Tokenizer), but it must be closed to indicate that it’s no more to be done with the underlying stream. The stream breaking is done in accordance with delimiter pattern (default is whitespaces, see Character.isWhitespace(char c)). The following example is taken directly from API documentation:

String input = "1 fish 2 fish red fish blue fish";
Scanner s = new Scanner(input).useDelimiter("\\s*fish\\s*");
System.out.print(s.nextInt() + " ");
System.out.print(s.nextInt() + " ");
System.out.print(s.next() + " ");
System.out.println(s.next());
s.close();

with the output (as you might expect)

1 2 red blue

Scanner allows to process input in accordance with specified Locale. The Scanner interface is quite big, but even the use of a small part of it can simplify your code substantially. Read API, Java Tutorial, the text books.

File Class and Directory Browsing

Doing the OS work from within a Java program

An application can interact with the underlying operation system. Often such interaction involves elements of the file system — files and directories. An application may require reading, writing and executing files, finding files, establishing and changing file properties, deleting files etc. As usual, Java must do this in the OS-neutral way, without using OS-specific commands and file-system properties. This type of problems are dealt with with help of File class and a set of global configuration values called properties (which include system properties).

A File object is virtual proxy for an underlying OS file; it allows to find out everything about a file and perform a number of operations on it:

and some others. As an example, this is how we can list a directory content for files which end with the chosen suffix (from examples DirectoryLister.java).

Example: Directory Listing from Within Java

import java.io.*;
import java.util.Properties;

class DirectoryLister {
    public static void main(String[] args) {
        final String suffix = args.length < 1 ? "" : args[0];
        //run-time directory name (different on different OS platforms)
        String cwdName = System.getProperty("user.dir");
        File cwd = new File(cwdName);  // cwd is File object standing for directory
        // (tricky) anonymous class instantiation
        FilenameFilter ffilter = new FilenameFilter() { 
            public boolean  accept(File f, String n) {
                return (n.endsWith(suffix)) ? true : false;
            }
        };
        if (cwd.isDirectory()) {
            System.out.println("The directory contains:");
            for (File file: cwd.listFiles(ffilter))
                System.out.printf("%s: %d\n", file.getName(), file.length());
        }
    }
}
ls -l *.pattern | awk '{ print $9": " $5 }' # this is the Unix CL solution :)

Java’s New I/O

For historical reasons, Java I/O is rather complicated and harder to use. Java 7 comes up with substantial improvements in dealing with I/O issues (so called NIO 2.0):

Path class

The class File from java.io allows in principle to fully manage the file system — navigate the file hierarchy, establish file properties, delete and rename files, link and unlink and so on. But it is not easy to get right (esp. symbolic links) and has problem with synchronisation (the file tree could change in the course of program execution, which would cause problems with navigating it via the absolute path). Most of this problem have the root in how a file on the disk is represented in the program as an object of the class File.

Instead of java.io.File all the file system manipulations (more reliably) and many new ones, can be performed with the new java.nio.file.Path class. The key novelty here is that JVM binds a Path object to the actual physical file (programmer’s jargon for a file on the disk) at the run-time. Foundation classes of the java.nio.file package include:

All in all — java.nio.file is a great new API asset; it’ll pay to learn how to use it, but not in this course (not yet)!

Methods and OO programming

Exceptions are natural in OO programming

method-exception

Java’s Exceptions

Advanced flow control

How to handle errors (including severe) which occur at run time? When a method is invoked on an object, but the is object in a wrong state, or data passed as parameters are wrong, or the method contract is otherwise violated — an erroneous event occurs which must be dealt with during the program execution. To test for all such possibilities (correctness) would be to hamper the code clarity. Exceptions is the way to achieve both correctness and clarity.

ex_callstack

Method call stack

ex_trace

Exception goes back

Exceptions Types

As everything in Java, exceptions are objects. Their classes extend either Exception or Error:

Exception Inheritance Tree

ex_tree

Exceptions: Client side (try-catch)

This is how an exception handler is written:

try {
    .... do smth which can throw an exception type caught below ....
} catch (ExceptionOne e1) {
    .... dealing with ExceptionOne ....
} catch (ExceptionTwo e2) {
    .... dealing with ExceptionTow     ....
}     .... <possibly more catch statements> ....
finally { // this part is optional, include it to avoid resource leaks and
      // to secure handling unchecked exceptions --- it's never bypassed
    .... <executes always when try block exits> ....
} ...
// anything else is executed only if no exceptions are thrown in try

An example from Java Tutorial for the method writeList() (in the class ListOfNumbers and ListOfNumbers2), which demonstrates the use of the clauses in an exception handler + two examples, WithoutFinally.java and WithFinally.java.

The order in which the caught exceptions are listed is important: if their types overlap (ie, one is a subtype of another, IOException → FileNotFoundException), the narrower (here, second) type must precede the wider (first) to avoid skipping a specialised response.

Exceptions: Method side (how to throw)

Exception objects are created when methods (or constructors) throw them:

modifiers return-type doSmth() throws ExceptionOne, ExceptionTwo, ... {
         ...........
     throw new ExceptionOne(); // instance of ExceptionOne class or its superclass
         ...........
     throw new ExceptionTwo(); // instance of ExceptionTwo class or its superclass
         ........... 
}

The throw statement can occur either in a standard if-else construct (which may test for a specific state of an object), or inside the catch block (which will result in a chain of exceptions thrown up to the next higher level exception handler).

The exception classes listed after throws represent a part of the full method signature. Unchecked exceptions, if not caught in “catch”-requirement, are not flagged by compiler. If your code invokes a method which was defined with throws clause for a checked exception, it must take care of catching it (using try-catch construct), or the compiler will flag an error. The unchecked exceptions do not require catching, though it’s often prudent to do so (example ListOfNumbers.java).

A method may choose to handle an exception by throwing an exception of its own, in which case the method caller will have to deal with it, which, if it handles it, will create an exception chain.

Exceptional chain of events

This is an example of an exception chain which takes place during the execution of ExceptionTest.java. Here, exceptions throw their own exceptions (propagate sideways), some exceptions are uncaught are returned back to their caller (forwarded exceptions).

  • main() (inside its try) calls m1()
  • m1() (inside its try) calls m2(), and then m3()
  • m2() doesn’t throw, executes its finally(), returns to m1()
  • m3() throws to m1(), executes its finally(), returns to m1()
  • m1() catches from m3(), re-throws to main(), executes its finally(), returns to main()
  • main() catches from m1(), terminates (“returns” to JVM)

ex_call_chain

Exceptional Issues

When an exception throwing method is overridden (in a subclass), exceptions which the superclass method has thrown can be “specialised”: ie, the throws clause of an overridden method can have fewer types listed than the superclass method, or more specific types, or both. It can even dispense with throwing exceptions altogether (no checked exceptions). Contrast this with “overriding” access modifiers: they can either extend method’s visibility, or leave it unchanged (protected or "package"public, but not other way around).

Some programmers question (not unreasonably) the usefulness of separation checked and unchecked exceptions (see, for example, articles cited in Tim McCune’s “Exception-Handling Anti-patterns”; this paper doesn’t directly discuss this, but instead gives a good advice on when to catch an exception and when to ignore it, when and how best to define your own exception class, how exceptions are abused, and how to avoid such a practice).

As to the checked-vs-unchecked issue (eg, C# doesn’t have such distinction), my opinion is that it’s not the distinction itself (it is useful since catching certain exceptions must be enforced at compilation), but why some exceptions are defined as checked (NoSuchMethodException…), or unchecked (EnumConstantNotPresentException…). Some exceptions which should be often (always?) caught (NumberFormatException, IndexOutOfBoundsException, etc) are unchecked, because otherwise “they would get too much in a way” (I’m simply guessing what API’s people thought -:).

Defining Exceptions

Exceptions are classes, and you can define your own exception to deal with a particular situation as you see fit. By defining a checked Exception class, every client which invokes a method or constructor which throws your exception, shall also try-catch it.

Advantages of exceptions — they allow simpler, more maintainable programs by

Exceptions vs return values

Correct use of exceptions usually takes longer then other aspects of OO programming. When is it better to use exceptions instead of “ordinary” procedural approach?

Use them in a really exceptional situation, not for ordinary control flow (think first before including try/catch inside loops, yet see ScannerExample.java) — it worsens performance, bad for clarity. For API, it’s a balancing act — to make a method return a special value to mark atypical event or to throw an exception (Integer.parseInt() vs InputStream.read()) — depends on concurrent access to object, synchronisation, performance etc.

End-of-input-stream is a regular event End-of-input-stream treated as exception
while ((token = stream.next()) !=
      stream.END) {
   process(token);
}
stream.close();
try {
    for (;;)
        process(stream.next());
} catch (StreamEndException e) {
    stream.close();
}

The flow of control in the first case is clear and simple. In the second case (which at first sight loops forever), even if you know about StreamEndException, the construction is confusing since the loop termination condition is moved outside.

Remark: when all values read from a stream are valid (non-exceptional), eg when reading from a stream of doubles, it’s a good idea to add an explicit eof() test method that should be called before reading next token.

Exceptions do-and-donts

Here some rules:

Exception Syntax Enhancement in Java 7

When a code can throw multiple exceptions, and one has to handle them all, the old Java way was to use multi-catch statements, like in the try-catch Slide 21. The catch statements must be properly ordered to allow exception-type specific handling (as discussed before). It can get cluttered (therefore, inefficient and error-prone).

When the exceptions can be sub-grouped, so each one within the same subgroup can be given a similar treatment, these sub-grouped exceptions can be handles by a single catch-statement. The following code (taken from Evans and Verburg’s book) discriminates between exceptions caused by input error (system unable to supply the resource) or data error (missing or bad file):

public Configuration getConfig(String fileName) {
   Configuration cfg = null;
   try {
      String fileText = getFile(fileName); 
      cfg = verifyConfig(parseConfig(fileText)); 
   } catch (FileNotFoundException|ParseException|ConfigurationException e) {
      System.err.println("Config file '" + fileName +
                    "' is missing or malformed");
   } catch (IOException iox) {
      System.err.println("Error while processing file '" + fileName + "'");
   }
   return cfg;
}

Issue of Resource Management

Another JDK 7 extension involving handling exception is resource management. Program resources are memory, I/O streams — opened files, sockets (network connections), pipes (program-to-program connections) and others. Memory (allocated for objects) is managed automatically by the garbage collector (great feature of Java), but other resources must be cared for manually by the programmer. Often, to guarantee the release of a stream resource (file opened for reading or writing etc) is difficult if between opening (acquiring resource) and closing (relinquishing resource) an exception can disrupt the normal flow of control, so that the resource management remains unfinished. That’s where finally statement is very useful, but… it may include its own try-catch-finally because, for example, closing a stream (the method OutputStream.close() and similar) can throw an IOException of its own.

Moreover, a code with multiple external streams may include nested try-statements. In the example MultipleFinally.java, data is read from an InputStream (a URL connection) and written to a OutputStream connected to a File. Opening and closing each of the three can throw an exception:

According to a JRS to Project Coin (the JDK 7 name), 2/3 of uses of close() in Java API (prior to JDK 7) were buggy.

Automatic Resource Management with try(...)

To clean up all this (mainly artificial) mess, Java 7 has introduced try-with-resources (TWR) syntax extension for the use of try. It allows to localise all opened streams (and acquired resources, in general) to the try-catch-finally block, such that when the block is exited (in whatever way — regular or exceptional) — the resources are relinquished (just like any local variable is destroyed and memory consumed by it released). The syntax:

try (OutputStream out = new FileOutputStream(file);
    InputStream is = url.openStream()) {
  byte[] buf = new byte[4096];
  int len;
  while ((len = is.read(buf)) > 0) {
    out.write(buf, 0, len);
  }
}

The resource acquisition is performed according to statements inside the parentheses and that’s it! No more care is necessary. (Subtlety: chaining the stream object creation via nested constructors isn’t recommended in TWRs, since some streams may not be closed due to their anonymity; better to assign every opened stream to a variable, like it’s done in the example).

TWR shamelessly borrowed the idea of automatic resource management through localisation inside a block from other languages. Python’s with-statement has almost identical semantics to TWR (apparently, C# does the same thing using using-clause, but I wouldn’t know -:).

Assertions: Design by contract in Java

The state of computational environment can be characterised by an invariant, which is a boolean value expression always evaluating to true. Assertions is the Java mechanism to check the invariant. It can be used at every step of computation as a test that everything goes “according to plan”. If the test fails (the invariant is false), the AssertError is thrown.

assert expr [:"Error message string"]; // the [...] (second expression) is optional
assert val > 0 && val < 20 : "the input value must be within the range";

Assertions are disabled by default. To enable them, run your application with -ea option

java -ea RootClass // enabling assertions across all classes
java -ea:package-name RootClass // enabling assertions in a package
java -ea:class-name RootClass // enabling assertions in a class

Assertions are turned on during development, and normally are turned off after the application is made available to customers (however, it’s advisable to keep them on since they greatly simplify the errors analysis).

A useful “hack” — to put an assertion in a place where the execution must never ever get into:

assert false; // the code will abort if this is reached!

Typeless interfaces

Types in Java cab be defined in two ways: abstract classes (partial implementation) and interfaces (pure contract). The main purpose of an interface is to define behaviour. The type defined in an interface can be used to refer to instances of the class, which implement that interface. But, Java also allows to define an interface without any behaviour, typeless. Before Java 1.5, such interfaces were used for types with a finite number of instances:

public interface PhysicalConstants { 
    static final double PLANCK_CONSTANT = ...;
    static final double ELECTRON_MASS = ...;
    static final double ELECTRON_CHARGE = ...;
       ... ... ...
}

Bad practice: use of constants is an implementation detail which leaks into the implementing classes; its use by clients has nothing to do with the type behaviour, and is confusing. Worse, constant interface represents a commitment: future class modifications which do not use the constants still require implementation for binary compatibility (design becomes ugly and illogical).

One solution for constants problem is to put them into a utility class with no public constructors:

public final class PhysicalConstants {
    private PhysicalConstants() { ... } //instantiation impossible
    public static final double PLANCK_CONSTANT = ...;
       .............
}

Interfaces: Type safe enumerations

Better solution is achieved through the use of type safe enumeration pattern for class definition. For example, the type which holds four suits of cards can be declared as such class as follows:

public class Suit { 
    private final String name; 
    private Suit(String name) { this.name = name; } 
    public String toString()  { return name; } 
    public static final Suit CLUBS    = new Suit("clubs"); 
    public static final Suit DIAMONDS = new Suit("diamonds"); 
    public static final Suit HEARTS   = new Suit("hearts"); 
    public static final Suit SPADES   = new Suit("spades"); 
} 
private static final Suit[] PRIVATE_VALUES = { CLUBS, DIAMONDS, HEARTS, SPADES }; 

When defined like this, there is no way for clients to create objects of the class or to extend it, there will never be any objects of the type besides those exported via the public static final fields (type safety not available through the use of typeless interfaces). Even though the class is not declared final, there is no way to extend it: Subclass constructors must invoke a superclass constructor, and no such constructor is accessible.

This very elegant solution (due to Joshua Bloch) is the classic example when a successful solution to a recurring problem (this is what software designers call a design pattern), can be promoted and made a language feature in its own right (not every design pattern can be utilised like this). The feature which implements the type safe enumerations is the enum.

Enumeration Types: enums

enums are the types, all instances of which are known when the type is defined:

enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES, } // named instances

This looks like a class declaration, and this is indeed a special kind of class disguised as a new language feature. In such primitive form, the usage of this definition of Suit is similar to the constant interface variant (without the negative implications). But enums can be used with much greater power (this is a class, after all). You can declare constructor and methods inside enum:

public enum Chess { 
  PAWN(1), BISHOP(10), KNIGHT(15), ROOK(20), QUEEN(100); // named constructors
  Chess(int value) { this.value = value; } // implicit private constructor 
  private final int value; // constant value tied to enum instance
  public int value() { return value; }
  public String printValue() {return value + ((value > 1) ? " points" : " point");}     
}

An Example: enumerated chess

The Chess example (the source code: Chess.java and ChessTest.java):

public class ChessTest { 
    public static void main(String[] args) { 
        System.out.println("You don't play a chess game to count points, but:");
        for (Chess c : Chess.values()) 
            System.out.println(c + " is worth " 
                + c.printValue() + ", and it's " + appearance(c)); 
    } 
    private enum ChessStature { SHORT, MEDIUM, TALL } // this is inner enum type
    private static ChessStature appearance(Chess c) { 
        switch(c) { // c can be only Number or enum
          case PAWN:    return ChessStature.SHORT; 
          case BISHOP: 
          case KNIGHT: 
          case ROOK:    return ChessStature.MEDIUM; 
          case QUEEN:   return ChessStature.TALL; 
          default: throw new AssertionError("Unknown figure: " + c); 
        } 
    } 
} 

Demo run to output. (Another example is Planets.java from Java Tutorial.)

Packages

This is the Java mechanism for finding and using types (classes, interfaces, enumes and annotations), for avoiding name conflicts, and for controlling the access to class members. Projects have a package structure which defines how related classes are bundled together. The Java APIs are organised as follows: fundamental classes are in java.lang, classes for reading and writing (input/output) are in java.io, useful (utility) classes are in java.util, (some) GUI classes are in javax.swing etc. JavaSE comes with more than 200 packages, and there are numerous specialised packages from third parties.

A class is defined as a package member via the package declaration in the beginning of every class (before the class declaration):

package comp6700.labs.lab1; //in every class you wrote in the lab 1
package comp6700.ass.ass1; //in all assignment 1 classes    

Remember that the type/member visibility increases in this gradation:

private →  <default> →  protected →  public

To make a type available to the program, use import declaration at the beginning of your source code (before the class declaration):

import comp6700.ass.assOne;//makes all classes from assOne available
// wildcard (*) does not mean *all*, compiler only imports those which are used
import javax.swing.*; 
// (since Java 1.5) one can also import static members from a class
import static packagename.Classname.staticMemberName; 

Intra-package access

package1

Trans-package access

package2

Package and protected access

package3

Compiling to package structure

Why to package?

To create the byte code for your application which has the correct mapping of the package structure to the file structure of compiled class files, use the -d option with javac command (DrJava’s casus). To make classes available during the compiling or execution, use -classpath option, or set CLASSPATH environment variable.

javac -d . comp6700/ass/ass1/MainClass.java 

This will compile the ass1 classes to their package structure.

From the java man page (run man java):

For example, if you specify "-d /home/myclasses" and the class is  
called com.mypackage.MyClass, then the class file is called 
/home/myclasses/com/mypackage/MyClass.class. If -d is not specified,
javac puts the class file in the same directory as the source file.

Generics: safer code

Generics is the language technique for treating a computational problem which involves one or several class types without specifying the actual classes involved. A class or a method, which involves manipulation of other classes or instances, can be defined in a way which only requires the validity of operations used for those classes while the classes themselves should be defined only at the time of the class instantiation or method invocation.

Prior to the Java 1.5 version, the only generic type was Array, “[ ]”, where the type of its elements could regarded as the parameter-type: E[] "=" []E. With all the quirkiness of such type definition, the structure of this type was very specific. Few other types (Vector, Collection, List, Hashtable) also had rudimentary parameterisation, which was achieved by treating every element of these structures as Object. By this, there was no mechanism to ensure that a data structure contained type-“homogeneous” elements. Even if all your elements in a Vector object were, say, Strings, when you extracted them, the explicit cast was required, since the return type of the extracted element was Object:

String str = (String) v.elementAt(i); // v is an instance of Vector class;

This was awkward (code clutter, annoyance), this was error prone (the programmer might have inserted an incompatible type into v etc). The generics introduce two major benefits:

Parameterised types

Since Java 1.5 you can define parameterised types, or generic classes — classes which involves other classes as parameters, not actual types. The actual type values are used during instantiation (similar to method’s invocation with actual parameters).

class A<T> { // more than one type parameter can be used, class A<E,K>
    private T field;
    public A (T param) {
        this.field = param;
    }
    public T getField() {
        return this.field;
    }
}

Instances of a generic class are defined with the type parameter given a value — an existing type:

A<String> aS = new A<String>("I get it!");
A<Integer> aI = new A<Integer>(100); // "100" means A<Integer>(100) --- autoboxing!

Type parameters are not types themselves! There is no T.java class defined anywhere in the API. This is why T (or any other symbol used) is not a part of the class A name (compiler removes generic information from the class definition — so called type erasure — and A.java → A.class). Examples: Cell.java, ClassTest.java.

Generics: Naming conventions

Naming guidelines: type parameter names are single, uppercase letters, which is quite different from the variable naming convention — the difference between type variable and an ordinary class or interface name should be very clear. The most commonly used type parameter names:

Syntax enhancement in JDK 7

Instantiating a generic container is now easier. Instead of

Map<String, List<Trade>> trades = new TreeMap<String, List<Trade>> ();

use the diamond operator <>:

Map<String, List<Trade>> trades = new TreeMap <> ();

and compiler will select the right type values from the left side.

Generic constructors and methods

Type parameters can also be used within method and constructor signatures to create generic methods and generic constructors. Similar to declaring a generic type, but the type parameter’s scope is limited to the method or constructor in which it’s declared.

public class BoxG<T> { //"borrowed" from Java Tutorial
    private T t;    
    public void add(T t) { this.t = t; }
    public T get() { return t; }

    public <U> void inspect(U u){ // a method with a parameterised parameter type
        System.out.println("T: " + t.getClass().getName());
        System.out.println("U: " + u.getClass().getName());
    }
    public static void main(String[] args) {
        BoxG<Integer> integerBox = new BoxG<Integer>();
        integerBox.add(new Integer(10));
        integerBox.inspect("some text");
    }
}

The output (BoxG.java):

T: java.lang.Integer
U: java.lang.String

Generic static methods

A more interesting use of generic static methods:

public static <U> void fillBoxes(U u, ArrayList<Box<U>> boxes) {
    for (Box<U> box : boxes) {
        box.add(u);
    } // assuming that this is a part of a non-generic class Box definition
}

The method usage may go like this:

Fruit pineapple = new Fruit("pineapple", 3.0); // creating a 3kg heavy pineapple
ArrayList<Box<Fruit>> fruitBoxes = new ArrayList<Box<Fruit>>();
Box.<Fruit>fillBoxes(pineapple, fruitBoxes);

Compiler can infer the type value of the parameter itself, so the explicit declaration isn’t often necessary:

Box.fillBoxes(pineapple, fruitBoxes); // compiler infers that U is Fruit.

(this is called type inference, it allows to invoke a generic method as if it was an ordinary one.)

Bounded Type Parameters, Sub-typing and Wildcards

A generic class definition may involve manipulations which do not make sense for every class, eg, addition of two instance of the parameter class. In such case, one would like to put a constraint on the class parameter values used in instantiation. In the case of the mentioned addition operation, which makes sense for Number types (subclasses of Number) and few others (like String). Such constraint is declared via bounded type parameters. The syntax (using the Box example above — demo in BoxBT.java):

public <U extends Number> void inspect(U u){ ... } // U is (sub-)type of Number
class CMP<T extends Number & Comparable<T>> { ... } // T is also comparable

After such bounding, the compilation will fail because the invocation integerBox.inspect("some text") is now illegal (the value of the type parameter U here is String which is not Number).

The generic class with bounded type parameters are also defined (which is more often used than bounded type methods).

Sub-typing of parameterised types: Integer is a subtype on Number, but Box<Integer> is not a subtype of Box<Number> (see diagram on the next slide). The method doSmth(Box<Number> n) will not accept Box<Integer> type actual parameter. The right way to declare such methods is to use wildcards ? (demo in BoxWC.java):

public void doSmth(Box<? extends Number> box) {... // upper bound wildcard
public void doSmth(Box<? super Integer> box) {... // lower bound wildcard

Taxonomy of generic types

A generic method to sum the elements of a generic List

static double sum(List<? extends Number> list ) {
    double sum = 0.0;
    for (Number n : list)
        sum += n.doubleValue();
    return sum;
}

The wildcard “?” indicates that the method sum requires a List of any subtype of Number (including itself) — the bounded wildcard with an upper bound.

taxonomy

A bounded wildcard with a lower bound List<? super Integer> matches any super-type of List<Integer> (including itself):

  1. List<Integer>
  2. List<Number>
  3. List<Serializable>
  4. List<Comparable<Integer>>
  5. List<Object>

The Feel of Java… gone sour?

Back in 1995… “The Feel of Java”

Java is a blue collar language. It’s not PhD thesis material but a language for a job. Java feels very familiar to many different programmers because we preferred tried-and-tested things (James Gosling “Feel of Java”, Talk at OOPSLA 1996).

After Generics became part of the language

  1. “I am completely and totally humbled. Laid low. I realise now that I am simply not smart at all. I made the mistake of thinking that I could understand generics. I simply cannot. I just can’t. This is really depressing. It is the first time that I’ve ever not been able to understand something related to computers, in any domain, anywhere, period.”

  2. “I’m the lead architect here, have a PhD in physics, and have been working daily in Java for 10 years and know it pretty well. The other guy is a very senior enterprise developer (wrote an email system that sends 600 million emails/year with almost no maintenance). If we can’t get [generics], it’s highly unlikely that the ‘average’ developer will ever in our lifetimes be able to figure this stuff out.” (both citations by Josh Bloch at JavaPolis 2007.)

If you want to become a Java’s generics expert, read Angelika Langer’s 427-page (!) Java Generics FAQ (also there is a book “Java Generics and Collections” by Maurice Naftalin and Philip Wadler, O’Reilly 2006). But first ask yourself: “Can I go to C++ or Scala instead?”

Generics intimidation

Cay Hortsmann (interview Java Champion, Feb 2008)

When students first see the API with thousands of classes, they despair. I used to be able to tell them, “That’s OK, at least the language itself is very simple.” But that was before this:

static <T extends Object & Comparable<? super T>> T 
                      Collections.max(Collection<? extends T> coll);

As a student, you need to stay within a safe subset of the Java language and the API so that you can use it as a tool to learn some good computer science.

Joshua Bloch (talk at JavaPolis2007): “We simply cannot afford another wildcards”.

“Typical” declarations and compiler errors and warnings involving generics:

Enum<E extends Enum<E>> { ... };
<T extends Object & Comparable<? super T>> 
              T Collections.max(Collection<? extends T> col) ;
public <V extends Wrapper<? extends Comparable<T>>> 
              Comparator<V> comparator() { ... };
error: equalTo(Box<capture of ?>) in Box<capture of ?> cannot 
              be applied to (Box<capture of ?>) 
    equal = unknownBox.equalTo(unknownBox) 
Arrays.asList(String.class, Integer.class) // Warning!

Java’s Reflections

Java’s doclets, annotation processing tools, the JUnit testing framework, class browsers (in IDEs) and many other Java related technologies — they all rely on the “ability of a running program to examine itself and its software environment, and to change what it does depending on what it finds” (I. & N. Formans, Java Reflections in Action).

This is a simplest example of this kind, when the program understands its own name. Without being overridden, the printName() method behaves differently for each subclass than it does for HelloWorld. The printName() method is flexible; it adapts to the class that inherits it, causing the change in behaviour.

public class HelloWorld {
  public void printName() {
    System.out.println(
           this.getClass().getName()); (2)
  }
}
...
HelloWorld x = new HelloWorld();
x.printName(); // (1)
HelloWorld // (3)

What’s happening?

  1. printName() examines object x for its class

  2. decision of what to print is delegated to the object’s class

  3. the method acts on this decision by printing the name

Reflections — link between the worlds

“To perform this self-examination, a program needs to have a representation of itself. This information is called metadata. In an object-oriented world, metadata are organized into objects, called metaobjects. The runtime self-examination of the metaobjects is called introspection.” The very style of writing programs which can understand and define their semantic structure is called metaprogarmming.

Metaobjects are instances of metaclasses, dwellers of yet higher (“Platonic”) world: Class, Array, Field, Method, Constructor, Modifierand others are metaclasses — the object-oriented elements of the Java metamodel. Java programs get their meaning through the meaning of metaobjects, and the metaobject semantics is provided through metaclasses. Things quickly get heady:

Heavens and Earth inside JVM

metaworld

Resetting private fields

Using metaobjects, one can change the program structure and behaviour: Method metaobjects can be commanded to invoke the methods they represent (dynamic invocation), Field and Constructor objects can be used to reflectively access and reset the program state variable, and create new ones. In principle, the reflections allow you to achieve the effect impossible via standard means, eg to access and change a value of private fileds: It is not possible to read and change the value of an object private field without getter-setter methods. Or is it?

HelloWorld x = new HelloWorld("magic_word", 42);
x.printMembers();
...
name = magic_word
number = 42
...
Field[] fields = x.getClass().getDeclaredFields();
// some code to establish that f1 is a private String and it's called "name"
Field f1 = fields[0]; 
f1.setAccessible(true);
f1.set(x,"mutabor");
x.printMembers();
...
name = mutabor
number = 42
...

Complete code is in TestHelloWorld.java and HelloWorld.java.

Cloning un-“cloneable” and other miracles

Let x is a name (reference) of an object defined elsewhere (for example sent alongside with a client request) we can find out a lot about x during run-time and use this information to choose what to do as we go.

Class<?> cl = x.getClass();
Constructor<?>[] constructors = cl.getDeclaredConstructors();
Field[] fields = cl.getDeclaredFields();
Method[] methods = cl.getDeclaredMethods();
// we can invoke a constructor to create a new (identical) object to x
xClone = constructors[0].newInstance(Object... initargs);
// which constructor to use from the constructors array cab be 
// meticulously computed as well as what parameters initargs to  
// use for recreating the current field values of the object x

It’s all quite tricky and even messy (at first sight), but we can recreate an exact copy (“clone”) of the object x in its current state even if x is an instance of a class which does not support cloning (ie, does not implement CloneNotSupportedException interface). The code and its discussion can be found in an article by Vladimir Roubtsov, in JavaWorld, January 2003.

Whys of Reflections

Reflections drawbacks:

Reflections warts: