Want to show your appreciation? Please to my charity.

Wednesday, June 28, 2006

Saturday, June 24, 2006

Submitted a enhancement request to Sun for optional type parameters

The RFE is as below:

Title

[Generics] Support optional type parameters

Introduction

In majority of use cases, the data type retrieved from a Map object is the same type of the data that was added to it. But there are exceptions, for example, the MultiMap and TransformedMap in the wildly adopted Jakarta Commons-Collections. With the current definition of the java.util.Map interface, it is impossible to parameterized the MulitMap while still maintain the backward compatibility with legacy code. Refining the java.util.Map as Map<K,V,I> can resolve the problem but then all existing parameterized subclass of Map must be changed.

Justification

It is a very common situation in the evolution of any application or code library that a new typed parameter needs to be added to an existing interface or class. This task becomes extermely difficult (if not at all impossible) with the limitation of current generics syntax. As we all familiar with adding overloaded methods to extend the functionality of an object, a similar mechanism in Generics is very much yearned. The description section above provided a example and there is a blog explains it in detail: http://kennethxu.blogspot.com/2006/06/is-definition-of-map-interface-overly.html

Expected Behavior

A default type can be assiged to a typed parameter. Examples:
class Foo<K,V=Object> {...} // means Object is used when V is omitted
Foo<String> foo; // equivalent to Foo<string,> foo;
interface Bar<K,V=K> {...} // means type K is used when V is ommited
Bar<String> bar; // equivalent to Bar<String,String> bar;

Test Code

A very simple test case:
public class GenericsEnhancement {
   class Foo<K,V=Object> {} // means Object is used when V is omitted
   class Bar<K,V=K> {} // means type K is used when V is ommited
   public void test() {
     Foo<String> foo1 = new Foo<String,Object>();
     Foo<String,Object> foo2 = new Foo<String>();
     Bar<String> bar1 = new Bar<String,String>();
     Bar<String,String> bar2 = new Bar<String>();
   }
}

Friday, June 23, 2006

Eclipse compiler bug: false ambiguous method error

This is an eclipse bug in the area of supporting JDK 1.5 Generics. The bug was discovered in 3.1.2. Not sure about 3.2 but I couldn't find similar bug report in bugzilla.

To reproduce the bug, use one of the class below. Both of them are compiled fine with javac 1.5.0_06.

Link to bugzilla: https://bugs.eclipse.org/bugs/show_bug.cgi?id=148504

List 1 - A theoretical example.

public class EclipseAmbiguousBug {
   interface IWorker<E> {
     void add(E e);
   }

   interface IPerson<E> {
     void add(E e);
   }

   interface IFather<E> extends IWorker<E>, IPerson<E>{   }

   interface IMother<E> extends IPerson<E> {
     void add(E e);
   }

   interface IChild<E> extends IFather<E>, IMother<E> {   }
   static class Child<E> implements IChild<E> {
     public void add(E e) {}
   }

   public static void main(String[] args) {
     IChild c = new Child();
     c.add(""); // correct warning
     IChild<String> cg = new Child<String>();
     cg.add(""); // false ambiguous error
   }
}

List 2 - A practical example.

import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

public class EclipseAmbiguousBug2 {
   interface Addable<E> {
     boolean add(E e);
   }
   interface XCollection<E> extends Collection<E>, Addable<E> {}
   interface XSet<E> extends XCollection<E>, Set<E> {}

   static class TestXSet<E> extends AbstractSet<E> implements XSet<E> {
     public boolean add(E e) {return false;}
     public Iterator<E> iterator() {return null;}
     public int size() {return 0;}
   }

   public static void main(String[] args) {
     XSet xs = new TestXSet();
     xs.add(""); // correctly generated warning
     XSet<String> xs2 = new TestXSet<String>();
     xs2.add(""); // false ambiguous error message
   }
}

Monday, June 19, 2006

Is the definition of Map interface overly restricted in Java 5

I spent entire yesterday evaluating the possibility of Generifying Commons-Collections and still keeps the backward compatibility. It turned out to be not as simple as I thought initially.

Java 5 type-safe Map and Collection strictly requires the data that you retrieve from it must be of the same type of the data you put into it. This becomes a major problem generifying a number of useful Map and Collection implementations in Commons-Collections. For example, MultiMap, TransformedMap. While my initial evaluation goal yet to be archived, what did come to my attention is a difficult question: is the definition of Map (similarly to Collection) interface overly restricted in Java 5?

Well, in the real life, I put an egg in incubator can get the chick back after sometime. So for what reason the get method must return the same type of data as the put(or add or set) method takes? Let's look at the refined Map definition:

public interface Map<K,V,I=V> {
     // Ok, the I=V is something that I made up here, but isn't it neat?
     // It means if I is not given, default to V, assuming our compiler
     // is smart enough to understand it.
     V get(Object o);
     I put(K key, I input);
}

This pseudo Map interface is less restricted so apparently to be much useful. Let's exam how MultiMap can be supported with the refined interface.

public abstract class AbstractHashMap<K,V,I=V> implements Map<K,V,I> {
     public V get(Object object) {
         return ...;
     }
     protected boolean putInternal(K key, V input) {
         // same logic was in put
     }
}
public class HashMap<K,V> extends AbstractHashMap<K,V> {
     public V get(Object object) {
         return ...;
      }
     public V put(K key, V input) {
         return putInternal(key, input)? input:null;
     }
}
public class MultiMap<K,V,I=V> implements Map<K,Collection<V>,I> {
     public Collection<V> get(Object o) {
         return ...;
     }
     public I put(K key, I input) {
     }
}
public abstract class AbstractMultiHashMap<K,V,I=V>
     extends AbstractHashMap<K, Collection<V>, I>
     implements MultiMap<K, V, I>
{
    protected boolean addInternal(K key, V value) {
         Collection<V> c = get(key);
         if(c==null) {
             c= new ArrayList<V>();
             putInternal(key, c);
         }
         c.add(value);
        return true;
     }
}
public class MultiHashMap<K,V> extends AbstractMultiHashMap<K,V> {
    public V put(K key, V input) {
         return addInternal(key, input)? input:null;
     }
}
Obviously, we need to handle setValue in Map.Entry too. This might make it a little complicated but clearly it's doable. EDIT: RFE is filed to Sun and accepted in it's bug databaes