Polymorphism in its purest form

The nature of abstract classes and polymorphism

Q: I would like to clarify my understanding of abstract classes.

As I understand it, a class declared abstract is an incomplete class, not meant to be instantiated. Java would complain if an abstract class were created using a class instance creation expression.

The javadoc for DateFormat provides two examples:

  1. myString = DateFormat.getDateInstance().format(myDate);
    
    
  2. DateFormat df = DateFormat.getDateInstance();
       for (int i = 0; i < a.length; ++i)
       { output.println(df.format(myDate[i]) + "; "); }
    
    

With that in mind, since DateFormat is an abstract class, how can an instance of it be created by the final static method DateFormat.getDateInstance() ?

A: It is absolutely true that you cannot instantiate an abstract class. You are correct in pointing out that DateFormat::getDateInstance() can not return an instance of DateFormat by calling new DateFormat(). Something else is going on here.

getDateInstance() is a factory method. Factory methods hide the implementation of the object that they create. Internally, they create a concrete extension of an abstract class (or interface), but cast it down to the interface or abstract class before it returns the instance.

Consider the following implementations of DateFormat:

  • AmericanDateFormat extends DateFormat. AmericanDateFormat formats dates in the following format: mm/dd/yyyy
  • EuropeanDateFormat extends DateFormat. EuropeanDateFormat formats dates in the following format: yyyy/mm/dd
  • StarDateFormat extends DateFormat. StarDateFormat formats dates in the following format: XXXXXX.XX

Since getDateInstance() cannot instantiate DateFormat, it must instantiate one of its nonabstract implementations — either AmericanDateFormat, EuropeanDateFormat, or StarDateFormat.

When we call getDateInstance(), we do not know which particular implementation of DateFormat is returned. That detail remains hidden from you. Instead, we simply treat the instance as the abstraction DateFormat. Once we have an instance, we use the behavior without worrying about the specific implementation and move on.

This is polymorphism in its purest form. By creating different implementations of DateFormat but treating each of them as the superclass, we have a type that displays many different behaviors. By taking such an approach, we can program very generically.

Say we didn’t use polymorphism but wanted to have different DateFormats in the same program. We would need to treat every implementation very specifically. We would need if statements or case statements for each specific implementation that we wish to use. Whenever we wanted to add new implementations, we would need to update all of our code.

With polymorphism, however, if we want to switch implementations, we need only to override the instance factory method so that it creates an instance of the implementation we want. We can make this change and use the new implementation without having to change any of the code that uses the instance. This allows us to plug new implementation types into our system at any time.

So, for example, after writing the code that uses the factory method, we could introduce an AztecDateFormat class and use it by only changing the factory code. We wouldn’t need to change anything else to use it!

Source: www.infoworld.com