Monday, February 8, 2010

Why Generics

Let's say you have an array of Process objects.
Process[] procs;

However, you find that an array has a fixed length and you wish to store only the processes that has been created within the latest one minute rolling window. Using an array as a FIFO pipe would also be rather inconvenient, so you decide to use a List. But then you would find that using a List, you could have no control over what other routines put into it before passing it to your routines. Therefore, you wish to make sure it is a List of Process objects only. Generics allow you to constrain
List<Process> proclist;

So that, now you can have a method to handle a list which you could ensure contains only Process objects:
public float meanCreationInterval(List<Process> proclist){
  .... }

Frequently, you also wish to constrain the contents of a Hashtable. The following would allow you to constrain your hashtable to contain only EmployeeId as keys, and PostalAddr as values:
Hashtable<EmployeeId, PostalAddr>

Let's say you have a base class with a method getEmployee(EmployeeId id)
class Employee {
  public Employee getPerson(EmployeeId id){ ....}
}

Then you extended Employee class for a specific case of Engineer

class Engineer extends Employee { ....}

But when you tried to use the parent getPerson method, you discovered, holy cow, it returns an Employee object but you want it to return an Engineer object. You could use casting, but frequently you receive an extended class as a base class and you have to write code to test what subclass it is to cast it. What if someone uses your library and passes it a subclass that you never knew existed? So you genericize your base class
class Employee<P extends Employee<?>>{
public P getPerson(EmployeeId id){ ....}
}

Then programmers would extend it as
class Engineer extends Employee<Engineer>{ ....}

So that, now getPerson method would return an Engineer object if the extension class is Engineer or a Manager object if the extension class is Manager, etc.

Let's say you have base classes Canvas and Window.
class Canvas<W extends Window<?>> {
  private W window;
  public W getWindow();
}
class Window<C extends Controller> {
  private C controller;
  public C getController { return controller;}
}

Then you have extension classes
class JoyStick implements Controller {
  public int getRate() { ... }
}
class KeyboardArrowKeys implements Controller {
  public float getPixels(){...}
}

MainWindow extends Window <JoyStick>{ ... }  
Popup extends Window <KeyboardArrowKeys>{ ... }  

Generics can be used to constrain not only extension of a class, but also the type arguments of the members of that class being extended
MainWindow win = new MainWindow();
Popup pop = new Popup();
win.getController()
  .getRate(); // allowed
win.getController()
  .getPixels(); // error: getPixels method not found

pop.getController()
  .getRate(); // error: getRate method not found
pop.getController()
  .getPixels(); // allowed

Generics can be defined as deep as is required to constrain members of any depth.
public abstract class Vehicle
<V extends Vehicle<?,?>, D extends Dashboard>
{  .... }

public class Dashboard
 <D extends Dashboard, S extends Speedo<?>>
{  .... }

public class Speedo<Sh extends Shape>
{  .... }

public class Shape<Sh extends Shape>
{  .... }

//Square needs any super class of Analogue
public class Square<D super Analogue>
  extends Shape<Square>
{  .... }

public class Analogue
  extends DispTech
{  .... }

public class Sedan
 extends Vehicle<
  Sedan,
  Dash<
   ?,
   Speedo<
    Shape<
     Square<DispTech>
    >
   >
  >
{  .... }

Define a Sedan Vehicle type,
but it must have a Dashboard,
where the Dashboard must have a Speedo,
and the Speedo must have Square as Shape,
and the Square needs DispTech as the specific super class of Analogue.

However, using generics for too deep number of levels can be very obfuscating.

No comments:

Post a Comment