类型T不是通用的;它不能用参数来参数化?通用函数中的>错误。

时间:2021-08-28 16:13:48

I want to create a generic function that takes any Map & a String key, if the key is not present in the map, then it should create a new instance of the Value Type (which is passed) & put it in the map & then return it.

我想创建一个通用函数,它接受任何映射&一个字符串键,如果在Map中不存在键,那么它应该创建一个值类型的新实例(已通过),并将其放入映射中,然后返回它。

Here is my implementation

这是我的实现

public <T> T getValueFromMap(Map<String, T> map, String key, Class<T> valueClass){
    T value = map.get(key);
    if (value == null){
        try {
            value = valueClass.newInstance();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        map.put(key, value);
    }
    return value;
}

It works if I use it with a normal (not generic) List as the value type,

如果我用一个普通的(非通用的)列表作为值类型,它就会起作用,

Map<String,List> myMap;
List value = getValueFromMap(myMap, "aKey", List.class) //works

but not with generic type lists

但不是用泛型类型列表。

Map<String,List<String>> myMap;
List<String> value = getValueFromMap(myMap, "aKey", List.class) //does not work

Also if I try to make Map<String, T> map parameter generic by changing it to Map<String, T<?>> it complains that the type T is not generic; it cannot be parameterized with arguments <?>

另外,如果我尝试用Map 映射参数泛型,将它改为Map >它抱怨T类型不是通用的;不能用参数 来参数化它。 ,> ,>

Can the generic parameters be themselves made generic?

通用参数本身是否可以通用?

Is there any way to create function with above mentioned requirements?

是否有方法可以用上述要求创建函数?

~Update

~更新

Thanks for all the insightful answers everyone.

感谢所有人的深刻见解。

I have verified that the accepted solution works for any value type with this code

我已经验证了所接受的解决方案适用于任何具有此代码的值类型。

    Map<String, Map<String, List<String>>> outer = 
            new HashMap<String, Map<String,List<String>>>();

    Map<String, List<String>> inner = getValueFromMap(outer, "b", 
            (Class<Map<String, List<String>>>)(Class<?>)HashMap.class);

    List<String> list = getValueFromMap(inner, "a", 
            (Class<List<String>>)(Class<?>)ArrayList.class);

4 个解决方案

#1


5  

You need most specific types which is String in your case. List.class returns you Class<List<?>> in your case you need specific type so you will need to add cast Class<List<String>>

您需要在您的案例中使用字符串的大多数特定类型。列表。< <列表类返回你课吗?在您的情况下,> >需要特定的类型,因此您需要添加cast类 >。

 Map<String,List<String>> myMap = new HashMap<String, List<String>>();;
 List<String> value = getValueFromMap(myMap, "aKey",
           (Class<List<String>>)(Class<?>)ArrayList.class) ;

Now if you invoke method like below

现在,如果您调用下面这样的方法。

 getValueFromMap(myMap, "aKey",      List.class) 
                ^^^T is List<String>  ^^^ T is List<?> wild char since List.class
                                          will return you Class<List<?>>

So your defination of T is causing confusion to Compiler so you are getting compile error.

所以你对T的定义会给编译器造成混乱所以你会得到编译错误。

You can not create instance of List.class since it is interface you will need implemention class of it so call method with ArrayList.class

您不能创建List的实例。类,因为它是接口,所以您需要它的实现类,因此调用方法与ArrayList.class。

#2


1  

You can not call generic methods with parameters that are themshelves generic as the generic type information is erased at runtime.

您不能调用泛型方法,因为在运行时通用类型信息会被清除。

See http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ004

看到http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html FAQ004

If you always use map of lists then may be you can declare the method to take Map<String, List<T>>

如果您总是使用列表的map,那么可能您可以声明该方法来获取map >。 ,>

#3


1  

The issue here is that you're asking for a Class<T> parameter, and there is no way to actually get a Class<List<String>> object (directly).

这里的问题是,您要求的是一个类 参数,并且没有办法实际获得类 >对象(直接)。

List.class gives you a Class<List>, but List<String>.class doesn't even compile (it's not syntactically correct).

列表。类为您提供了一个类 ,但是List 。类甚至不能编译(它不是语法正确的)。

The workaround is to cast the class object yourself into the generic version. You'll need to upcast first as otherwise Java will complain that the types aren't assignable:

解决方法是将类对象转换为泛型版本。您需要先向上转换,否则Java将会抱怨这些类型是不可分配的:

Class<List<String>> clz = (Class<List<String>>)(Class<?>)List.class;

Since generics are implemented via erasure, the casting doesn't fundamentally do anything - but it keeps the compiler happy with its inference of what T is, and allows flexible generic methods like yours to work. This is not an uncommon workaround to need to use when you want to pass in a Class<T> token.

由于泛型是通过删除来实现的,所以转换不会从根本上做任何事情——但是它使编译器对T的推断感到满意,并且允许像您这样的灵活的通用方法来工作。当您想要传递一个类 令牌时,这不是一个不常见的方法。

#4


1  

The first example workds becase you are using raw type that is not recomended.

第一个示例workds becase,您使用的是未被重用的原始类型。

To solve your compilation error you might want to not limit the class into T but allow somethi g super the T

为了解决编译错误,您可能希望不要将类限制为T,但是允许一些超级的T。

public <T> T getValueFromMap(Map<String, T> map, String key, Class<? super T> valueClass)

public < t> T getValueFromMap(Map Map, String key, Class valueClass) ,>

But then you will have not save convertion and you will need to cast result of new instance into (T). And you can not use List as the class because is an iterface and you can not create a instace of it.

但是,您将没有保存转换,您将需要将新实例的结果转换为(T)。您不能使用List作为类,因为它是一个iterface,您不能创建它的一个instace。

public <K,V> V getValueFromMap(Map<K, V> map, K key, Class<? super V> valueClass){

    if(map.containsKey(key) == false) {
        try {
            map.put(key, (V) valueClass.newInstance());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    return map.get(key);
}

Map<String,ArrayList<String>> myMap = new HashMap<String, ArrayList<String>>();
List<String> value = getValueFromMap(myMap, "aKey", ArrayList.class); //will work

EDIT

编辑

But what about the base form Map<String,List<String> myMap, that OP expect ?

但是基础表单Map myMap, OP期望是什么? ,list

As we should know ArrayList is super type of List, but ArrayList<String> is not super type of List<String> but it is assignable.

我们应该知道ArrayList是超级类型的列表,但是ArrayList 不是超级类型的List ,但是它是可赋值的。

So lets try it out:

我们来试试

Example 1: getValueFromMap(myMap, "aKey", ArrayList.class); //Compilation error

示例1:getValueFromMap(myMap,“aKey”,ArrayList.class);/ /编译错误

Example 2: getValueFromMap(myMap, "aKey", List.class); //Runtime error

示例2:getValueFromMap(myMap,“aKey”,List.class);/ /运行时错误

Solution for this might be some assumption that if class is List then create ArrayList.

解决方案可能是一些假设,如果类是List,那么创建ArrayList。

This force us to create some not decent code.

这迫使我们创建一些不雅的代码。

public static <V> V createInstace(Class<V> valueClass) throws InstantiationException, IllegalAccessException {

        if(List.class.isAssignableFrom(valueClass)) {
            return (V) new ArrayList<V>();
        }

        return valueClass.newInstance();

    }

This code do not really have anything common with generics but is working.

这段代码与泛型没有任何共同之处,但它正在工作。

At the end we finish with

最后我们结束了。

public static void main(String[] args) {

        Map<String,List<String>> myMap = new HashMap<String, List<String>>();
        List<String> value = getValueFromMap(myMap, "aKey", List.class); //does not work

        value.add("Success");

        System.out.println(value);
    }


    public static <K,V> V getValueFromMap(Map<K, V> map, K key, Class<? super V> valueClass){

        if(map.containsKey(key) == false) {
            try {
                map.put(key, (V) createInstace(valueClass));
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        return map.get(key);
    }

    public static <V> V createInstace(Class<V> valueClass) throws InstantiationException, IllegalAccessException {

        if(List.class.isAssignableFrom(valueClass)) {
            return (V) new ArrayList<V>();
        }

        return valueClass.newInstance();

    }

And the result if this is [Sucess].

如果这是成功的话。

#1


5  

You need most specific types which is String in your case. List.class returns you Class<List<?>> in your case you need specific type so you will need to add cast Class<List<String>>

您需要在您的案例中使用字符串的大多数特定类型。列表。< <列表类返回你课吗?在您的情况下,> >需要特定的类型,因此您需要添加cast类 >。

 Map<String,List<String>> myMap = new HashMap<String, List<String>>();;
 List<String> value = getValueFromMap(myMap, "aKey",
           (Class<List<String>>)(Class<?>)ArrayList.class) ;

Now if you invoke method like below

现在,如果您调用下面这样的方法。

 getValueFromMap(myMap, "aKey",      List.class) 
                ^^^T is List<String>  ^^^ T is List<?> wild char since List.class
                                          will return you Class<List<?>>

So your defination of T is causing confusion to Compiler so you are getting compile error.

所以你对T的定义会给编译器造成混乱所以你会得到编译错误。

You can not create instance of List.class since it is interface you will need implemention class of it so call method with ArrayList.class

您不能创建List的实例。类,因为它是接口,所以您需要它的实现类,因此调用方法与ArrayList.class。

#2


1  

You can not call generic methods with parameters that are themshelves generic as the generic type information is erased at runtime.

您不能调用泛型方法,因为在运行时通用类型信息会被清除。

See http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ004

看到http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html FAQ004

If you always use map of lists then may be you can declare the method to take Map<String, List<T>>

如果您总是使用列表的map,那么可能您可以声明该方法来获取map >。 ,>

#3


1  

The issue here is that you're asking for a Class<T> parameter, and there is no way to actually get a Class<List<String>> object (directly).

这里的问题是,您要求的是一个类 参数,并且没有办法实际获得类 >对象(直接)。

List.class gives you a Class<List>, but List<String>.class doesn't even compile (it's not syntactically correct).

列表。类为您提供了一个类 ,但是List 。类甚至不能编译(它不是语法正确的)。

The workaround is to cast the class object yourself into the generic version. You'll need to upcast first as otherwise Java will complain that the types aren't assignable:

解决方法是将类对象转换为泛型版本。您需要先向上转换,否则Java将会抱怨这些类型是不可分配的:

Class<List<String>> clz = (Class<List<String>>)(Class<?>)List.class;

Since generics are implemented via erasure, the casting doesn't fundamentally do anything - but it keeps the compiler happy with its inference of what T is, and allows flexible generic methods like yours to work. This is not an uncommon workaround to need to use when you want to pass in a Class<T> token.

由于泛型是通过删除来实现的,所以转换不会从根本上做任何事情——但是它使编译器对T的推断感到满意,并且允许像您这样的灵活的通用方法来工作。当您想要传递一个类 令牌时,这不是一个不常见的方法。

#4


1  

The first example workds becase you are using raw type that is not recomended.

第一个示例workds becase,您使用的是未被重用的原始类型。

To solve your compilation error you might want to not limit the class into T but allow somethi g super the T

为了解决编译错误,您可能希望不要将类限制为T,但是允许一些超级的T。

public <T> T getValueFromMap(Map<String, T> map, String key, Class<? super T> valueClass)

public < t> T getValueFromMap(Map Map, String key, Class valueClass) ,>

But then you will have not save convertion and you will need to cast result of new instance into (T). And you can not use List as the class because is an iterface and you can not create a instace of it.

但是,您将没有保存转换,您将需要将新实例的结果转换为(T)。您不能使用List作为类,因为它是一个iterface,您不能创建它的一个instace。

public <K,V> V getValueFromMap(Map<K, V> map, K key, Class<? super V> valueClass){

    if(map.containsKey(key) == false) {
        try {
            map.put(key, (V) valueClass.newInstance());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    return map.get(key);
}

Map<String,ArrayList<String>> myMap = new HashMap<String, ArrayList<String>>();
List<String> value = getValueFromMap(myMap, "aKey", ArrayList.class); //will work

EDIT

编辑

But what about the base form Map<String,List<String> myMap, that OP expect ?

但是基础表单Map myMap, OP期望是什么? ,list

As we should know ArrayList is super type of List, but ArrayList<String> is not super type of List<String> but it is assignable.

我们应该知道ArrayList是超级类型的列表,但是ArrayList 不是超级类型的List ,但是它是可赋值的。

So lets try it out:

我们来试试

Example 1: getValueFromMap(myMap, "aKey", ArrayList.class); //Compilation error

示例1:getValueFromMap(myMap,“aKey”,ArrayList.class);/ /编译错误

Example 2: getValueFromMap(myMap, "aKey", List.class); //Runtime error

示例2:getValueFromMap(myMap,“aKey”,List.class);/ /运行时错误

Solution for this might be some assumption that if class is List then create ArrayList.

解决方案可能是一些假设,如果类是List,那么创建ArrayList。

This force us to create some not decent code.

这迫使我们创建一些不雅的代码。

public static <V> V createInstace(Class<V> valueClass) throws InstantiationException, IllegalAccessException {

        if(List.class.isAssignableFrom(valueClass)) {
            return (V) new ArrayList<V>();
        }

        return valueClass.newInstance();

    }

This code do not really have anything common with generics but is working.

这段代码与泛型没有任何共同之处,但它正在工作。

At the end we finish with

最后我们结束了。

public static void main(String[] args) {

        Map<String,List<String>> myMap = new HashMap<String, List<String>>();
        List<String> value = getValueFromMap(myMap, "aKey", List.class); //does not work

        value.add("Success");

        System.out.println(value);
    }


    public static <K,V> V getValueFromMap(Map<K, V> map, K key, Class<? super V> valueClass){

        if(map.containsKey(key) == false) {
            try {
                map.put(key, (V) createInstace(valueClass));
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        return map.get(key);
    }

    public static <V> V createInstace(Class<V> valueClass) throws InstantiationException, IllegalAccessException {

        if(List.class.isAssignableFrom(valueClass)) {
            return (V) new ArrayList<V>();
        }

        return valueClass.newInstance();

    }

And the result if this is [Sucess].

如果这是成功的话。