Friday, 26 February 2010

Tricky-use-of-static-initializer-in Java - Override println

Can we get a different output without changing the main-method definition?


public class HelloMain {

/**
* @param args
*/
public static void main(String[] args) {
System.out.println("Mr. main.");
}

}


Can we have the output of the above code-segment as "Hi, Mr. main. Bye!" without changing the main-method code?

Yeah, we can have the required output by using 'static initializer' blocks effectively. As we know that a static initializer block is called when a class is loaded into memory and hence it'll obviously run before the main method. Let's see how this can be used here.

The required output can be broken into three pieces - "Hi, ", "Mr. main.", and " Bye!". These parts should get printed in this order only which means the string "Hi, " should be printed before the 'println' call inside the main method prints "Mr. main." and subsequently the last string " Bye!" should be printed.

As we know that 'println' method by default ends with a new line and hence to have the last part (" Bye!") being printed in continuation with the main-method println string, we probably have only two ways:-

1. Re-define the default 'println' behavior - we can create an anonymous subclass (as we would not be requiring that anywhere else) of the PrintStream class and then re-define the 'println' method as per our needs.
2. Changing the default line separator - we can use the third part " Bye!" as the new line separator (it can be passed as a command-line argument while calling the class) in which case the 'println' of the 'main' would print this as the line separator and we will end up getting the required output.

Solution #1: Overriding 'println' in a static initializer block in Java


import java.io.PrintStream;

public class HelloMain
{

static
{
// as we are using System.out as the output stream in main
final PrintStream currentOut = System.out;

// anonymous as we would need this sub class here only
PrintStream newOut = new PrintStream(currentOut)
{
// Overriding 'println' method
public void println(String string)
{

// Printing Part - 1 first
print("Hi, ");

//Printing Part -2
print(string);

// Printing Part - 3, but this should use original 'println' def
// and hence the usage of 'super' comes here.
super.println(" Bye!");
}
};

// Now we are ready with the modified PrintStream and hence setting that

System.setOut(newOut);

}

/**
* @param args
*/
public static void main(String[] args) {
System.out.println("Mr. main.");
}
}


Output: below is the screenshot displaying the output of Solution #1
Hi, ", "Mr. main. Bye!

Solution #2: using an appropriate 'line separator' with a static initializer block


public class HelloMain {

static
{
//Printing Part - 1
System.out.print("Hi, ");
}

/**
* @param args
*/
public static void main(String[] args) {
System.out.println("Mr. main."); //Printing Part - 2
}
}


Part - 3 is printed by changing the default line separator, which is done by setting the string " Bye!" as the new line separator using command line arguments.


java -Dline.separator=" Bye!" HelloMain


Output: below is the screenshot displaying the output of Solution #2

Hi, ", "Mr. main. Bye!

No comments:

Post a Comment