analytics

Thursday, March 31, 2011

J2SE 5.0 (Tiger) Features

These are the main features of J2SE 5.0 (Tiger)

Generics

When you take an element out of a Collection, you must cast it to the type of element that is stored in the collection. Besides being inconvenient, this is unsafe. The compiler does not check that your cast is the same as the collection's type, so the cast can fail at run time.

Generic provides a way for you to communicate the type of a collection to the compiler, so that it can be checked. Once the compiler knows the element type of the collection, the compiler can check that you have used the collection consistently and can insert the correct casts on values being taken out of the collection.

How to Use Generics

1. Find a data structure that accepts Object(s)

• ArrayList, LinkedList, HashMap, HashSet
• Not arrays: they already take explicit types!

2. Declare the data structure with the type(s) in angle brackets immediately after class name

• ArrayList myStrings = new ArrayList();
• HashMap myHashtable = new HashMap();

3. Insert objects of the appropriate type

• myStrings.add("Some String");
• myHashtable.put("Some Key", "Some Value");

4. No typecast required on removal

• String myValue = myStrings.get(0);
• String myOtherValue = myHashtable.get("Some Key");


ArrayList Example: Explicit Typecasts

ArrayList list = new ArrayList();
list.add(0, new Integer(42));
int total = ((Integer)list.get(0)).intValue();

The cast to Integer on the last line is an example of the typecasting issues that generic types aim to prevent. The issue is that the 1.4.2 Collection API uses the Object class to store the Collection objects, which means that it cannot pick up type mismatches at compile time. The first notification of a problem is a ClassCastException at runtime.

The same example with the generified Collections library is written as follows:

ArrayList list = new ArrayList();
list.add(0, new Integer(42));
int total = list.get(0).intValue();


Automatic Boxing and Unboxing of Primitive Types

As any Java programmer knows, you can’t put an int (or other primitive value) into a collection. Collections can only hold object references, so you have to box primitive values into the appropriate wrapper class (which is Integer in the case of int). When you take the object out of the collection, you get the Integer that you put in; if you need an int, you must unbox the Integer using the intValue method. All of this boxing and unboxing is a pain, and clutters up your code. The autoboxing and unboxing feature automates the process, eliminating the pain and the clutter.

Before

ArrayList list = new ArrayList();
list.add(0, new Integer(42));
int total = (list.get(0)).intValue();

After

ArrayList list = new ArrayList();
list.add(0, 42);
int total = list.get(0);



The For-Each Loop

The Iterator class is used heavily by the Collections API. It provides the mechanism to navigate sequentially through a Collection. The new enhanced for loop can replace the iterator when simply traversing through a Collection as follows. The compiler generates the looping code necessary and with generic types no additional casting is required.

The for (:) loop is designed for iteration over array and collections.

Iterating over Arrays:

for (;;) Loop
int[] ageInfo = {12, 30, 45, 55};
int sumAge = 0;
for (int i = 0; i < ageInfo.length; i++)
sumAge += ageInfo[i];

for (:) Loop

int[] ageInfo = {12, 30, 45, 55};
int sumAge = 0;
for (int element : ageInfo)
sumAge += element;

Note that an array element of a primitive value cannot be modified in the for(:) loop.

Iterating over non-generic Collections:

for(;;) Loop

Collection nameList = new ArrayList();
nameList.add("Tom");
nameList.add("Dick");
nameList.add("Harry");
for (Iterator it = nameList.iterator();
it.hasNext(); ) {
Object element = it.next();
if (element instanceof String) {
String name = (String) element;
//...
}
}

for(:) Loop

Collection nameList = new ArrayList();
nameList.add("Tom");
nameList.add("Dick");
nameList.add("Harry");
for (Object element : nameList) {
if (element instanceof String) {
String name = (String) element;
//...
}
}


Static Import

An import statement allows you to give a hint to the compiler as to which class you are
referring to in your code. It gives you the convenience of using the short name for a class
(like JButton) instead of the long, fully qualified name (like javax.swing.JButton).
Remember that import does not tell the compiler where the class resides (classpath does
that). It only tells the compiler which class you mean to use.

Syntax:

// Static-import-on-demand: imports all static members
import static FullyQualifiedTypeName.*;
// Single-static-import: imports a specific static member
import static FullyQualifiedTypeName.StaticMemberName;

Avoiding the Constant Interface Antipattern

Constant Interface

package mypackage;

public interface MachineStates {
// Fields are public,
// static and final.
int BUSY = 1;
int IDLE = 0;
int BLOCKED = -1;
}


Without Static Import

class MyFactory implements
mypackage.MachineStates {
public static void main(String[] args) {
int[] states = {IDLE, BUSY, IDLE, BLOCKED };
for (int s : states)
System.out.println(s);
}
}

With Static Import

import static mypackage.MachineStates.*; // Imports all static members.

class MyFactory2 {
public static void main(String[] args) {
int[] states = { IDLE, BUSY, IDLE, BLOCKED };
for (int s : states)
System.out.println(s);
}
}

Static-import-on-demand: Import of All Static Members

Without Static Import

class Calculate1 {
public static void main(String[] args) {
double x = 10.0, y = 20.5;
double squareroot = Math.sqrt(x);
double hypotenue = Math.hypot(x, y);
double area = Math.PI * y * y;
}
}


With Static Import

import static java.lang.Math.*;

// All static members from Math are imported.
class Calculate2 {
public static void main(String[] args) {
double x = 10.0, y = 20.5;
double squareroot = sqrt(x);
double hypotenue = hypot(x, y);
double area = PI * y * y;
}
}

Single-static-import: Import of Individual Static Members

import static java.lang.Math.sqrt; // Static method
import static java.lang.Math.PI; // Static field
// Only specified static members are imported.
class Calculate3 {
public static void main(String[] args) {
double x = 10.0, y = 20.5;
double squareroot = sqrt(x);
double hypotenue = Math.hypot(x, y); // Requires type name.
double area = PI * y * y;
}
}


Enums

Enumerated Types

• An enumerated type defines a finite set of symbolic names and their values.
• Standard approach is the int enum pattern (or the analogous String enum pattern):

public class MachineState {
public static final int BUSY = 1;
public static final int IDLE = 0;
public static final int BLOCKED = -1;
//...
}

public class Machine {
int state;
public void setState(int state) {
this.state = state;
}
//...
}

public class IntEnumPatternClient {
public static void main(String[] args) {
Machine machine = new Machine();
machine.setState(MachineState.BUSY); // (1) Constant qualified by class name
machine.setState(1); // Same as (1)
machine.setState(5); // Any int will do.
System.out.println(MachineState.BUSY); // Prints "1", not "BUSY".
}
}

Some Disadvantages of the int Enum Pattern

• Not typesafe.
– Any int value can be passed to the setState() method.

• No namespace.
– A constant must be qualified by the class (or interface) name, unless the class is extended (or the interface is implemented).

• Uninformative textual representation.
– Only the value can be printed, not the name.

• Constants compiled into clients.
– Clients need recompiling if the constant values change.

Typesafe Enum Construct

• The enum construct provides support for enum types:

enum MachineState { BUSY, IDLE, BLOCKED } // Canonical form
enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

• Keyword enum is used to declare an enum type.

• Overcomes all the disadvantages of the int enum pattern, and is more powerful

Properties of the Enum Type


• An enum declaration is a special kind of class declaration:
– It can be declared at the top-level and as static enum declaration.

// (1) Top level enum declaration
public enum SimpleMeal
{ BREAKFAST, LUNCH, DINNER }


public class EnumTypeDeclarations {
// (2) Static enum declaration is OK.
public enum SimpleMeal
{ BREAKFAST, LUNCH, DINNER };
public void foo() {
// (3) Local (inner) enum declaration is NOT OK!
enum SimpleMeal
{ BREAKFAST, LUNCH, DINNER }
}
}

Enum Constructors

• Each constant declaration can be followed by an argument list that is passed to the
constructor of the enum type having the matching parameter signature.

– An implicit standard constructor is created if no constructors are provided for the enum type.

– As an enum cannot be instantiated using the new operator, the constructors cannot be called explicitly.

public enum Meal {
BREAKFAST(7,30), LUNCH(12,15), DINNER(19,45);
Meal(int hh, int mm) {
assert (hh >= 0 && hh <= 23): "Illegal hour.";
assert (mm >= 0 && hh <= 59): "Illegal mins.";
this.hh = hh;
this.mm = mm;
}

// Time for the meal.
private int hh;
private int mm;
public int getHour() { return this.hh; }
public int getMins() { return this.mm; }
}

Methods Provided for the Enum Types

• Names of members declared in an enum type cannot conflict with automatically generated member names:

– The enum constant names cannot be redeclared.

– The following methods cannot be redeclared:

static []values()

Returns an array containing the constants of this enum class, in the order they are declared.

static valueOf(String name)

Return the enum constant with the specified name


• Enum types are based on the java.lang.Enum class which provides the default behavior.

• Enums cannot declare methods which override the final methods of the java.lang.Enum class:

– clone(), compareTo(Object), equals(Object), getDeclaringClass(), hashCode(), name(), ordinal().

– The final methods do what their names imply, but the clone() method throws an CloneNotSupportedException, as an enum constant cannot be cloned.

• Note that the enum constants must be declared before any other declarations in an enum type.

public class MealClient {
public static void main(String[] args) {
for (Meal meal : Meal.values())
System.out.println(meal + " served at " + meal.getHour() + ":" + meal.getMins() + ", has the ordinal value " +  meal.ordinal());
}
}


Output from the program:

BREAKFAST served at 7:30, has the ordinal value 0
LUNCH served at 12:15, has the ordinal value 1
DINNER served at 19:45, has the ordinal value 2


Enums in a switch statement


• The switch expression can be of an enum type, and the case labels can be enum constants of this enum type.

public class EnumClient {
public static void main(String[] args) {
Machine machine = new Machine();
machine.setState(MachineState.IDLE);
// ...
MachineState state = machine.getState();
switch(state) {
//case MachineState.BUSY:// Compile error: Must be unqualified.
case BUSY: System.out.println(state + ": Try later."); break;
case IDLE: System.out.println(state + ": At your service."); break;
case BLOCKED: System.out.println(state + ": Waiting on input."); break;
//case 2: // Compile error: Not unqualified enum constant.
default: assert false: "Unknown machine state: " + state;
}
}
}


Varargs


Assume you want to invoke a method with variable number of arguments. The option we
had on hand in Java 1.4 or earlier was to pass an array. Let’s consider a simple example
as shown below:

public static int max(int[] values)
{
int max = values[0];
for(int aValue : values)
{
if (aValue > max) max = aValue;
}
return max;
}

We can invoke this method by passing an array with different number of values as illustrated below:

max(new int[] {1, 7, 2, 9, 8});
max(new int[]{8, 12, 87, 23, 1, 3, 6, 9, 37});

In order to invoke a method that takes variable number of arguments, we had to bundle
the parameters into an array. While this works, it is not elegant. The varargs introduced in
Java 5 addresses the elegance issue.

It is not only elegant, it also is very type safe. Let’s take the max method and modify it to use the varargs.

public static int max(int... values)

Notice that the only thing I changed is int[] to int… and I did not modify the
implementation of the max method. If the previous implementation of the method still
works after the change to the parameter type, then what is the new syntax and how is it
related to the array?

A type followed by … is simply a syntax sugar–it’s nothing but an array. However, the
compiler allows you to pass either an array or a discrete set of values to this method. For
example, now I can call the modified max method as follows:

max(new int[] {1, 7, 2, 9, 8});
max(new int[]{8, 12, 87, 23, 1, 3, 6, 9, 37});

max(1, 7, 2, 9, 8);
max(8, 12, 87, 23, 1, 3, 6, 9, 37);

There’s less clutter in the bottom two lines than in the top two lines. But what’s going on
when you pass discrete values? The compiler simply rolls the values into an array. So, the

code:

max(1, 7);
is compiled into:
max(new int[] {1, 7});

In the above example we passed an array of int. What if we want to pass different types
of data? Sure we can. Consider the example below:

public static void print(Object... values)
{
for(Object obj : values)
{
System.out.printf("%s is of type %s\n", obj, obj.getClass().getName());
}
}

The above code receives a varargs of type Object. You can invoke it with different types as shown below:

print(1, "test", 'a', 2.1);

The output from this call is:

1 is of type java.lang.Integer test is of type java.lang.String a is of type java.lang.Character
2.1 is of type java.lang.Double


The first line should be no surprise if you kept autoboxing in mind–that is the reason for
the type to be Integer instead of int. We’ve also used the printf statement which
directly benefits from the varargs concept.

You are not restricted to having only varargs as parameters, that is, you can have regular
parameters and varargs, if you like. However, varargs, if present, must be trailing. In
other words, place any regular parameters you like and then place the varargs as shown in

the following example:

public static void print(String msg, Object... values)

Pros and Cons:

• varargs comes in handy when you want to pass variable number of arguments.
Use it if you need that flexibility and elegance.

• You loose some compile time type safety if your varargs is of type Object.
However, if it is specific type (like int…), then you have type safety.

• If in your application you expect only three or four parameters to be passed to the
method, then don’t bother to use varargs.

• If you are not careful, you may have trouble with method overloading as the
varargs may increase the chance of parameter collision when methods are overloaded.


Annotations

Annotations is a new feature from Java 5. Annotations are a kind of comment or meta data you can insert in your Java code. These annotations can then be processed at compile time by pre-compiler tools, or at runtime via Java Reflection. Here is an example of class annotation:

@MyAnnotation(name="someName", value = "Hello World")
public class TheClass {
}

The class TheClass has the annotation @MyAnnotation written ontop. Annotations are defined like interfaces. Here is the MyAnnotation definition:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)

public @interface MyAnnotation {
public String name();
public String value();
}

The @ in front of the interface marks it as an annotation. Once you have defined the annotation you can use it in your code, as shown in the earlier examples.

The two directives in the annotation definition, @Retention(RetentionPolicy.RUNTIME) and@Target(ElementType.TYPE), specifies how the annotation is to be used.

@Retention(RetentionPolicy.RUNTIME) means that the annotation can be accessed via reflection at runtime. If you do not set this directive, the annotation will not be preserved at runtime, and thus not available via reflection.

@Target(ElementType.TYPE) means that the annotation can only be used ontop of types (classes and interfaces typically). You can also specify METHOD or FIELD, or you can leave the target out alltogether so the annotation can be used for both classes, methods and fields.

Class Annotations

You can access the annotations of a class, method or field at runtime. Here is an example that accesses the class annotations:

Class aClass = TheClass.class;
Annotation[] annotations = aClass.getAnnotations();
for(Annotation annotation : annotations){
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
}

You can also access a specific class annotation like this:

Class aClass = TheClass.class;
Annotation annotation = aClass.getAnnotation(MyAnnotation.class);
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}

Method Annotations

Here is an example of a method with annotations:

public class TheClass {
@MyAnnotation(name="someName", value = "Hello World")
public void doSomething(){}
}

You can access method annotations like this:

Method method = ... //obtain method object
Annotation[] annotations = method.getDeclaredAnnotations();
for(Annotation annotation : annotations){
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
}

You can also access a specific method annotation like this:

Method method = ... // obtain method object
Annotation annotation = method.getAnnotation(MyAnnotation.class);
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}


Parameter Annotations

It is possible to add annotations to method parameter declarations too. Here is how that looks:

public class TheClass {
public static void doSomethingElse(
@MyAnnotation(name="aName", value="aValue") String parameter){
}
}

You can access parameter annotations from the Method object like this:

Method method = ... //obtain method object
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Class[] parameterTypes = method.getParameterTypes();
int i=0;
for(Annotation[] annotations : parameterAnnotations){
Class parameterType = parameterTypes[i++];
for(Annotation annotation : annotations){
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("param: " + parameterType.getName());
System.out.println("name : " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
}
}

Notice how the Method.getParameterAnnotations() method returns a two-dimensionalAnnotation array, containing an array of annotations for each method parameter.

Field Annotations

Here is an example of a field with annotations:

public class TheClass {
@MyAnnotation(name="someName", value = "Hello World")
public String myField = null;
}

You can access field annotations like this:

Field field = ... //obtain field object
Annotation[] annotations = field.getDeclaredAnnotations();
for(Annotation annotation : annotations){
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
}

You can also access a specific field annotation like this:

Field field = ... // obtain method object
Annotation annotation = field.getAnnotation(MyAnnotation.class);
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}


Other features

In this article we have focused on the language features in Java 5. Java 5 has some other

interesting features as well. Listed below are some select features:

• StringBuilder
– StringBuffer eliminates object creation overhead, but has synchronization overhead
– StringBuilder removes that overhead

• Client vs. Server side differences in garbage collection, more adaptive collection

• Improved Image I/O for performance and memory usage

• Reduced application startup time and footprint using shared archive

• Enhancement to Thread Priority

• Ability to get stack trace for a thread or all threads

• UncoughtExceptionHandler on a Thread

• Improved error reporting on fatal exceptions

• System.nanoTime() for nanoseconds granularity for time measurements

• ProcessBuilder

– Easier than Runtime.exec() to start process

• Formatter and Formattable provide ability to format output in printf like style

• Scanner for easier conversion to primitive types – based on regex

• java.lang.instrument allows byte code enhancement at runtime to instrument code

• Collections Framework has Queue, BlockingQueue, and ConcurrentMap interfaces and implementations. Some classes modified to implements new interfaces

• Reflection API supports annotation, enum. Class has been generified

• System.getenv() undeprecated

No comments:

Post a Comment