6

Consider this HashMap extention (generates an instance of the V class when calling "get" if it's null)

public class HashMapSafe<K, V> extends HashMap<K, V> implements Map<K, V>{

    private Class<V> dataType;

    public HashMapSafe(Class<V> clazz){
        dataType = clazz;
    }
    @SuppressWarnings("unchecked")
    @Override
    public V get(Object key) {
        if(!containsKey(key)){
            try {
                put((K)key, dataType.newInstance());
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return super.get(key);
    }
}

The usage of it is something like this

Map<String,Section> sections = new HashMapSafe<String,Section>(Section.class);
sections.get(sectionName); //always returns a Section instance, existing or new

It seems to me redundant a little to supply "Section" twice, once as a generic type, and also supply it's class. I assume it's impossible, but is there to implement HashMapSafe, (keeping the same functionality) so it can be used like this?

Map<String,Section> sections = new HashMapSafe<String,Section>();

Or like this?:

Map<String,Section> sections = new HashMapSafe<String>(Section.class);
Eran Medan
  • 44,555
  • 61
  • 184
  • 276
  • So, a google search for 'java newInstance generic class' turned up several old posts involving this.getClass() followed by an ability to access the generics of the object and therefore get the Class of those generics, suggesting your first option is possible. See: http://stackoverflow.com/questions/75175/create-instance-of-generic-type-in-java/75345#75345 – jbrookover Apr 03 '11 at 03:48

5 Answers5

6

You cannot improve the constructor usage due to type erasure as others have already pointed out, but you should be able to improve verbosity by using a static factory method instead of a constructor...

I am not in front of compiler and I can never get method type parameters right on first try, but it will go something like this...

public static <K,V> Map<K,V> create( Class<V> cl )
{
    return new HashMapSafe<K,V>(cl);
}

...

Map<String,Section> sections = HashMapSafe.create(Section.class);
Konstantin Komissarchik
  • 28,879
  • 6
  • 61
  • 61
3

No, there isn't. Java doesn't have Reified generics.

Guava has however a nice solution in flavor of MapMaker#makeComputingMap()

Map<String, Integer> numbers = new MapMaker().makeComputingMap(new Function<String, Integer>() {
    public Integer apply(String key) {
        return 0;
    }
});

Which sets and returns 0 instead of null when the key is not present (and it is threadsafe, as opposed to your solution).

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
1

Neither are possible. The first would require being able to do something like new V() and that can't be done. The second would require being able to set the V type at runtime because it's passed in the constructor, which also can't be done. Keep in mind that generics are only used a compile time and they're erased for runtime.

WhiteFang34
  • 70,765
  • 18
  • 106
  • 111
1

I found this article interesting: Reflecting generics

in short using:

  public abstract class AbstractUserType<T> implements UserType {
    ...
    public Class returnedClass {
      ParameterizedType parameterizedType =
        (ParameterizedType) getClass().getGenericSuperClass();
     return (Class) parameterizedtype.getActualTypeArguments()[0];
    }
    ...
  } 
manji
  • 47,442
  • 5
  • 96
  • 103
  • Note that this only works when you declare a fixed generic type straight in the class like `class MyMap extends MyAbstractMap`. I imagine that the OP would like to keep his class generic. – BalusC Apr 03 '11 at 03:46
  • You can keep the class generic you just have you declare you class like this `new HashMapSafe() {}` note the extra `{}` You can make the class abstract to avoid forggeting to do this. – Peter Lawrey Apr 03 '11 at 07:12
0

Is there a way to avoid the constructor passing the Class?

In short, No.

The root cause is "type erasure". At runtime, the implementation of HashMapSafe has no implicit knowledge of the types that correspond to the type parameters. That information is erased. If you need the implementation to know what the type is, you need to pass it as a Class object that is compatible with Class<V>.

Map<String,Section> sections = new HashMapSafe<String,Section>();

That doesn't work because of type erasure; see above.

Map<String,Section> sections = new HashMapSafe<String>(Section.class);

That doesn't work because the Java language requires you to specify a type (or wildcard) for all of the type parameters when you use that syntax.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216