5
\$\begingroup\$

I was reading Joshua Blochs "Effective Java 2nd edition" item 29, which describes how to create Heterogeneous containers, in the end of chapter he wrote:

A Class object used in this fashion is called a type token. You can also use a custom key type. For example, you could have a DatabaseRow type representing a database row (the container), and a generic type Column as its key.

It wondered me and I wrote this implementation which he has mentioned. Is there something I could have done better ? I'm mostly interested in Column class which serves as Type Token

class Column<T> {

    @SuppressWarnings("unchecked")
    public T cast(Object obj) {
        return (T) obj;
    }
}

public class DatabaseRow {

    private Map<Column<?>, Object> row = new HashMap<>();

    public <T> void putColumn(Column<T> type, T instance) {
        row.put(type, type.cast(instance));
    }

    public <T> T getColumn(Column<T> type) {
        return type.cast(row.get(type));
    }

    public static void main(String[] args) {
        DatabaseRow db = new DatabaseRow();

        Column<Integer> colInt = new Column<>();
        Column<Double> colDouble = new Column<>();

        db.putColumn(colInt, 1);
        db.putColumn(colDouble, 10.0);

        System.out.println(colInt.getClass() + " " + colDouble.getClass());
        System.out.println(db.getColumn(colInt) + " " + db.getColumn(colDouble));
    }

}
\$\endgroup\$
4
\$\begingroup\$

There are two things I would like to point out here.

  1. There is no need to do the cast on the putColumn call. The method signature is: public <T> void putColumn(Column<T> type, T instance) { so we know that instance is of type T already, and it is being stored in an Object value in a Map. There is no reason to force the cast before storing it.

  2. The Column's cast method would be better done using a Class<T> instance. Consider the 'standard' pattern of requiring a class instance in the constructor of the Column. For example:

    class Column<T> {
    
        private final Class<T> valClass;
        public Column(Class<T> valClass) {
            this.valClass = valClass;
        }
    
        public T cast(Object obj) {
            return obj == null ? null : valClass.cast(obj);
        }
    }
    

    this has the benefit of not requiring the SuppressWarnings. It also ends up being helpful for other reasons too.

    The class would be used as:

    Column<String> col = new Column(String.class);
    

    Apart from that, things will be the same.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.