Tuesday, September 1, 2009

[Java] Mixing Generic with Legacy Code

This is kinda weird and troublesome. Even though as of Java 5 introduced generics to create type-safe collections, mixing this new kind of code with older non-generic one can be very tricky. Sun obviously did not want to make pre-Java 5 code redundant so it created a few ways to make these two types of code play nicely. For example:
List<String> l = new ArrayList<String>();  seems to create a type-safe arraylist polymorphically but mind you the JVM doesn't see the generic declaration at runtime; Using generics is ONLY a type of compile-time protection. But why ? Why did they do this ? The answer is simple - to mix this new code with legacy code; so at runtime the JVM only sees List l = new ArrayList(); . Look what happens if a developer is high and isn't careful when tampering with older code in the following example:

import java.util.*;

public class TestBadLegacy {
    public static void main(String[] args) {
          List<Integer> myList = new ArrayList<Integer>();
          myList.add(4);
          myList.add(6);
          Inserter in = new Inserter();
          in.insert(myList); // pass List<Integer> to legacy code
         
          /*
          for (int i=0;i<myList.size();i++){
              System.out.println(myList.get(i));
          }
          */
         
         
          for(Integer i:myList){
              System.out.println(i);
          }
         
         
       }
}
/*
this is the supposedly generic type-safe class, but check out below the legacy Inserter class which has a method that doesn't know
that a type-safe collection is being passed to it
*/

class Inserter {
      // method with a non-generic List argument
      void insert(List list) {
         list.add(new String("42")); // adds to the incoming list
    }
}

The result of running TestBadLegacy is:
4
6
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    at TestBadLegacy.main(TestBadLegacy.java:18)

Surprised ? you shouldn't. even though a String element has been added to an integer-only collection the compiler didn't complain because the adding took place in the legacy code but at runtime when we did the foreach we assigned at one poin an Integer reference to a String object so this resulted in a complete meltdown.

Bollocks!