Extract Generic Type T from HashMap

79 views Asked by At

Is there a way to extract the T Type from a Java HashMap? We have different TranslationModels that all need to be prepared before they can be used. So therefore I created this generic Class which is supposed to handle all the different TranslationModels. The generic Type T holds the different TranslationModels.

package ch.lepa.app.zep.util;

import java.util.HashMap;
import java.util.List;

import ch.lepa.app.zep.bean.PersistenceHelperBean;
import ch.lepa.app.zep.model.LanguageModel;

public class LanguageSetup<T> {

    private HashMap<String, T> processedTranslations;
    private Class<T> type;

    public LanguageSetup(HashMap<String, T> unprocessedTranslations) throws Exception {
        // Extract the generic T class from the HashMap to set the Type
        type = //Extracted T class;
        processedTranslations = setupTranslationModel(unprocessedTranslations);
    }

    public HashMap<String, T> setupTranslationModel(HashMap<String, T> translation) throws Exception {

        PersistenceHelperBean phb = new PersistenceHelperBean();
        List<LanguageModel> languages = phb.getLanguages();

        for (LanguageModel language : languages) {
            if (!translation.containsKey(language.getCode())) {
                translation.put(language.getCode(), type.newInstance());
            }
        }
        return translation;
    }

    public HashMap<String, T> getProcessedTranslations() {
        return processedTranslations;
    }

    public void setProcessedTranslations(HashMap<String, T> processedTranslations) {
        this.processedTranslations = processedTranslations;
    }
}
2

There are 2 answers

2
Eran On

The generic type parameters are erased at compile time.

Therefore you must pass the type as an argument to your constructor in order to be able to create instances of that type :

public LanguageSetup(HashMap<String, T> unprocessedTranslations, Class<T> type) throws Exception {
    // Extract the generic T class from the HashMap to set the Type
    this.type = type;
    processedTranslations = setupTranslationModel(unprocessedTranslations);
}
0
Jorn Vernee On

You must understand that since Java uses type erasure, the type T is not saved in the HashMap object, those <...> are just there as an annotation to force certain compile time errors when you use the wrong type, and so the compiler can insert casts behind the scenes.

It looks like the only purpose for the Class<...> is so you can instantiate the type T. You could instead pass a functor which calls the constructor for T, and since Java 8 this is really simple.

Declare the constructor like:

public LanguageSetup(HashMap<String, T> unprocessedTranslations,
        Supplier<T> constructor) throws Exception {...}

And you'll be able to use it like:

new LanguageSetup(someHashMap, SomeType::new);

I should add that if T is parametrized, like List<String>, you can use something like ArrayList::new and it will automatically find the parameter.

Then you call get on this Supplier<...>, and it calls the constructor for you.

translation.put(language.getCode(), constructor.get());

It does not throw exceptions either which is nice.