views:

833

answers:

4

Hi there.. I need to create a Custom Hashtable extends java.lang.Hashtable and i need to override the get method to achieve the following behavior :

  1. if the key == null, it will return a new object of the type V
  2. if the super.get(key) == null, it will also return a new object of type V.

Can anyone help me. I try to do this but I know it's wrong.

import java.util.Hashtable;

public class CustomHashtable<K, V> extends Hashtable {
    @Override
    public synchronized V get(Object key) {
     if(key == null) return new Object();
     Object v = super.get(key);
     if(v == null){
      return new Object();
     }
    }

}

please see the line :

if(key == null) return new Object();

and the lines :

if(v == null){
    return new Object();
}

to know where the error occurred..

+8  A: 

You'd have to store the class related to V and create a new instance. For example:

public class CustomHashtable<K, V> extends Hashtable {
    Class<V> clazz;

    public CustomHashtable(Class<V> clazz) {
        this.clazz = clazz;
    }

    @Override
    public synchronized V get(Object key) {
        if(key == null) return newValue();
        Object v = super.get(key);
        if(v == null){
            return newValue();
        }
    }

    private V newValue() {
        try {
            return clazz.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException (e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException (e);
        }
    }
}

(You may want to change the exception handling of course.)

An alternative is to make the caller effectively provide a factory to create a new instance of V. You'd do this with an interface such as:

public interface Factory<T> {
    T create();
}

You could then store the factory in the custom hashtable, and call create any time you needed to.

Jon Skeet
Don't tell them to use reflection!!!!!!!eleventyone
Tom Hawtin - tackline
Thank Jon Skeet very much...Your help will be appreciated ....Thanks again...
Saeed
A: 

I understand what you asked, but could I ask the following please:

  • Do you always want a new object when the key is null, or don't you just want to allow a null key?
  • Also, do you definitely need a new instance when you can't find the key, or will the same instance do in each case where you can't find the key?
  • Are you going to put the new instances into the Hashtable?
  • Does it have to be a Hashtable, or could a HashMap do?

I'm just wondering if you had considered using a LazyMap from Apache Commons Collections?

A_M
Thanks A_M, the LazyMap idea is a good also, i don't know any thing about it before. Thank you for your Help
Saeed
+1  A: 

Do you need to create a new instance? or is it sufficient to return a default instance?
The latter could be implemented like:

public class CustomHashtable<K, V> extends Hashtable<K, V> {

    /** Default instance. */
    private final V defaultValue;

    public CustomHashtable(V defaultValue) {
        this.defaultValue= defaultValue;
    }

    @Override
    public synchronized V get(Object key) {
        if(key != null) {
            V val = super.get(key);
            if(val != null) {
                return val;
            }
        }
        return defaultValue;
    }
}

(but I still prefer Jon's factory solution: more flexible and also covers the default instance solution)

Carlos Heuberger
Thank Carlos HeubergerYour idea is a good idea also...Thanks again...
Saeed
+3  A: 

The primary issue here is what you are trying to achieve is fundamentally wrongheaded. Checkout the methods of your class. The majority of them will now be inconsistent with get. Worse, exactly how methods are implemented in terms of other public methods is not defined - such is the curse of inheritance.

Therefore, create a class that represents whatever abstraction you are trying to achieve. Have the contain not inherit from an appropriate map implementation.

The natural map in this case is probably not ye olde Hashtable but java.util.concurrent.ConcurrentHashMap. The important method here is [putIfAbsent][2]. Unfortunately the API docs suck. Here is how it should be used:

public V getOrCreate(K key) {
    final V value = map.get(key);
    if (value != null) {
        return value;
    }
    V newValue = factory.create(key); // May discard.
    V oldValue = map.putIfAbsent(key, value);
    return oldValue==null ? newValue : oldValue;
}

(You can use a Future if you want to ensure that you never discard a value.)

To create I've assumed some kind of an abstract factory. In general methods don't have no-args public constructors that happen not to throw exceptions. Certainly avoid reflection like swine flu crossed with H5N1. Instead use an appropriate (abstraction-specific) abstract factory passed in at creation time.

public interface MySortOfFactory<
    T /*extends SomeEntity*/,
    A /*extends SomeInfo*/
> {
    T create(A arg);
}

[2]: http://java.sun.com/javase/6/docs/api/java/util/concurrent/ConcurrentMap.html#putIfAbsent(K, V)

Tom Hawtin - tackline