Saturday, July 18, 2009

Accessing local variables in local classes

In Java, local classes can access any members of the containing class. However when it comes to local variables and method parameters they can use only the ones marked final.  The reason is that the instances of the class can live longer than the method.  And since local variables and method parameters live on the stack, they may be long gone before the instance actually uses them.  So if the local classes want to use local variables, they must have a private copy of all the local variables they use. The compiler automatically makes a private internal copy for the local class. However the value of the local variable can change during the lifetime of the method. So someone has to keep the private local variable of the local class in sync with the actual local variable. There are many issues involved in doing it (like when or how often should the sync take place etc). The safest and perhaps the easiest way to guarantee that the local and the private copy are same is to mark the local variable final.  Hence the local classes cannot use local variables and method parameters that are not marked final.  Here is an example of how an instance of a local class can save the scope and use it at a later point in time:





public class LocalClass {

private int instanceVariable = 0;
private interface LocalClassHolder{
int getInstanceVariable();
int getLocalVariable();
}

public LocalClassHolder testLocalClass() {

final int localVariable = 1;
LocalClassHolder lch;
int changingLocalVariable = 0; //not possible to use this in local class

class InnerLocalClass implements LocalClassHolder {
//return instance variable
public int getInstanceVariable() {
return instanceVariable;
}

//return local variable
public int getLocalVariable() {
return localVariable;
}
}
lch = new InnerLocalClass();
changingLocalVariable = 1000;
return lch;

}

public static void main(String[] args) {
LocalClass lc = new LocalClass();
LocalClassHolder ch = lc.testLocalClass();
int instance = ch.getInstanceVariable();
int local = ch.getLocalVariable();
System.out.println("Instance: " + instance + " " + "Local: " + local);

}

}


Sunday, July 12, 2009

Method overloading with Widening, Boxing and Varargs

With the introduction of boxing and varargs in Java 1.5, there are new rules to remember wrt overloaded methods that include widening, boxing and varargs. Key rule to remember is widening always wins over boxing and varargs when present individually. So mathematically, widening > boxing > varargs in that order. There is a catch with widening reference variables. The reference variable widening should satisy the IS-A test. Here is a simple example:



public class BoxWidenVarArgs {

public static void pickMe(Integer i) {
System.out.println("Integer ");
}
public static void pickMe(int... i) {
System.out.println("varargs int ");
}
public static void pickMe(short i) {
System.out.println("short");
}
public static void pickMe(Byte i) {
System.out.println("Byte ");
}
public static void pickMe(byte... i) {
System.out.println("varargs byte");
}
public static void onlyInteger(Integer i) {
System.out.println("Integer");
}

public static void main(String[] args) {
byte b = 1;
pickMe(b); //picks widening over boxing and varargs
int i = 10;
pickMe(i); //As no widening is present, picks boxing over varargs
/**
* compilation error, cannot widen from Byte to Integer as Byte
* is not a Integer, IS-A test fails!
*/
//onlyInteger(new Byte(b));
}

}




Ouput:
short
Integer

Here are possible combinations of Widening, boxing and varargs:

Combining Widening and Boxing:
Can we widen and box, is this allowed? The rule to remember is that first widening then boxing ( i.e. Widen+Box) is not allowed. So a short can be widened to an int but a short cannot be widened and boxed to an Integer.


public class WidenAndBox {

public static void pickMe(Integer i) {
System.out.println("Integer");
}
public static void main(String[] args) {
short s = 2;
//Compilation error. Not allowed.
//pickMe(s);

}

}




Combining Boxing and Widening:
First boxing and then widening is allowed provided the widening satisfies the IS-A relationship. So a short can be boxed to a Short and then widened to a Number or Object but a short cannot be boxed to a Short and then widened to a Integer or String because it does not satisfy the IS-A relation.

Combining varargs with widening/boxing:
You can widen and then combine varargs. You can box and combine with varargs. However you cannot have overloaded methods that has widening+varargs and boxing+varargs. You can combine varargs with either widening or boxing.


public class LegalCombinations {

public static void pickMe(Number n) {
System.out.println("Number");
}
public static void wideningVarArgs(Integer... i) {
System.out.println("Integer...");
}
public static void boxingVarArgs(long... i) {
System.out.println("long...");
}

public static void main(String[] args) {
short s = 2;
pickMe(s); //Boxing and Widening
int i = 1;
wideningVarArgs(i); //combination of widening and varargs
boxingVarArgs(i); //combination of boxing and varargs
}

}



The following causes compilation error as discussed above:
public class IllegalCombinations {

public static void withVarArgs(Integer... i) {
System.out.println("Integer...");
}
public static void withVarArgs(long... i) {
System.out.println("long...");
}

public static void main(String[] args) {
int i = 1;
/**
* Compilation error. Cannot have 2 overloaded methods one using
* boxing+varargs and another using widening+varargs.
* Can have one or the other.
*/
//withVarArgs(i);
}

}

Also remember varargs are always chosen last. So if you have 2 overloaded methods one with boxing + widening and and one with varargs, compiler will choose boxing+widening over varargs.

Summary
:
  • used individually, widening > boxing > varargs
  • varargs are always chosen last, they always lose.
  • reference variables have to satisfy IS-A test while widening
  • widening then boxing is not permitted. 
  • boxing then widening is permitted.
  • varargs can be combined with wither boxing or widening.

Monday, July 6, 2009

Shadowing final variables

In Java, final variables can be shadowed. They follow the same rules of shadow variables and static methods which I had discussed in my earlier post. The key point to remember is that while resolving a shadow variable or shadow static method, the reference type and NOT the reference object (i.e. object pointed to by the reference type) is used. Here is a simple example:

class Super {
public final String y = "SUPER";
public static final int z = 0;
}

class Sub extends Super {

//Shadowing a final variable
public final String y = "SUB";

//Shadowing a final variable and also changing type and access modifier's
char z = '1';

}

public class ShadowingFinalVariables {


public static void main(String[] args) {
Sub sub = new Sub();
Super supr = sub;


print(sub.y); //yields SUB
print(supr.y); //yields SUPER
print(((Super)sub).y); //Due to casting, yields SUPER
print(((Sub)supr).y); //Due to casting, yields SUB
print(sub.z); //yields 1
print(supr.z); //yields 0
print(((Super)sub).z); //Due to casting, yields 0
print(((Sub)supr).z);//Due to casting yields 1

}


public static <T> void print(T t) {
System.out.println(t);
}

}

Wednesday, July 1, 2009

Task Scheduling in Java


In Java, the Timer class helps you to schedule tasks to run at some time in the future. The tasks can be run either once or repeatedly like a batch job. 


Tasks can be scheduled to run in the following way:

  • Create Timers using a simple constructor of the Timer class. This creates a Timer Thread.
  • Subclass the TaskTimer class to represent your task. Implement the run() method and put your task implementation details here.
  • Schedule the task using one of the schedule() methods of the Timer class.


Types of Schedule

There are 3 types of schedules:

  • One time schedule

This runs the task once after the delay time specified. Here is an example:



import java.util.Timer;

import java.util.TimerTask;


public class MyTimer {

public static void main(String[] args) {

Timer timer = new Timer();

TimerTask task = new TimerTask() {

public void run() {

System.out.println("Scheduled Task");

}

};

//Execute this task after 0 milliseconds i.e. immediately

timer.schedule(task,0);

}


}

  • Fixed-delay schedule

This runs the task repeatedly after the initial delay time specified. The time between the subsequent executions is specified by a period time. This schedule ensures that the period time elapses between invocations. Here is one example:


long oneday = 24*60*60*1000;

//Execute this task every day starting immediately. Make sure

//that there is a 24 hr delay between the invocations. So if the

//last invocation was delayed for some reason, the delay carries

//forward.

timer.schedule(task, 0, oneday);


  • Fixed-rate schedule


This runs the task repeatedly after the initial delay time specified. The time between the subsequent executions is specified by a period time. This schedule ensures that the task runs during their scheduled time .i.e it is not affected by the time of its previous invocation. Example:

//Way to setup a cron or batch job. Make sure that the task 

//runs at the fixed time every 24 hrs. If the last invocation

//was delayed due to some reason, the delay is not reflected in the

//next invocation.

timer.scheduleAtFixedRate(task,0,oneday);


Some interesting facts:

  • Each Timer object runs only one background thread. So if multiple tasks are assigned to the same Timer object, they will be run sequentially. To run them in parallel you have to create multiple Timer objects. 
  • The class is thread-safe.
  • There is no mechanism to cancel the currently executing task. There is a cancel method which terminates the Timer thread and thereby discards any scheduled tasks but does not affect the currently executing task.