简体   繁体   English

如何让Java为我的类型安全地图文字推断出正确的通用地图类型?

[英]How do I make Java to infer the right generic map type for my typesafe map literals?

I am trying to write a typesafe map literal helper class in java that can generate maps of any type and have the compiler check that all given keys and values conform to the type of the map. 我试图在java中编写一个类型安全的地图文字辅助类,它可以生成任何类型的地图,并让编译器检查所​​有给定的键和值是否符合地图的类型。 For a map with two values there would be a function with the following signature: 对于具有两个值的地图,将有一个具有以下签名的函数:

public static <KEY, VALUE> Map<KEY, VALUE> make(final KEY key1,
        final VALUE value1,
        final KEY key2,
        final VALUE value2)

Now I would have expected that something like this would be possible: 现在我希望这样的事情是可能的:

    Map<Integer, Object> map = make(1, "bla", 3, 17);

However I get an compiler error: 但是我收到编译器错误:

Type mismatch: cannot convert from Map<Integer,Object&Serializable&Comparable<?>> to 
 Map<Integer,Object>

Is there a way to fix this? 有没有办法解决这个问题? Of course, defining a function with signature make(Object... keysAndValues) would work, but I would loose the compile time typesafety. 当然,使用签名make(Object... keysAndValues)定义一个函数会起作用,但是我会放弃编译时类型安全。 :-( :-(

The error is probably here: 错误可能在这里:

Map<Integer, Object> map = make(1, "bla", 3, 17);
                                   ^^^^^     ^^

VALUE > Object can't be inferred from both String and Integer VALUE > Object无法从StringInteger推断出VALUE > Object

Try any of these: 尝试以下任何一个:

// Helping the compiler to infer Object for VALUE
Map<Integer, Object> map = make(1, (Object)"bla", 3, 17);

// Explicitly bind VALUE to Object
Map<Integer, Object> map = MyClass.<Integer, Object>make(1, "bla", 3, 17);

// If you don't need to write into "map" (as user pmnt also suggested)
Map<Integer, ?> map = make(1, "bla", 3, 17);

Your code will work if you modify the Map's signature in the calling method: 如果您在调用方法中修改Map的签名,您的代码将起作用:

No changes required to make 无需更改make

public static <A, B> Map<A, B> make(final A key1, final B value1,
        final A key2, final B value2) {
    Map<A, B> map = new HashMap<A, B>();
    map.put(key1, value1);
    map.put(key2, value2);
    return map;
}

Make 's caller Make的呼叫者

You must change Map<String, Object> to Map<String, ? extends Object> 您必须将Map<String, Object>更改为Map<String, ? extends Object> Map<String, ? extends Object> : Map<String, ? extends Object>

public static void main(String[] args) {

    @SuppressWarnings("unused")
    Map<String, ? extends Object> m = make("a", new Integer(1), "2", "efg");

}

EDIT1: OpenJDK 1.6 + Eclipse Indigo compiler EDIT1:OpenJDK 1.6 + Eclipse Indigo编译器

EDIT2: When building generic maps like that, you must accept that you have to downcast when it comes to retrieving the values. 编辑2:在构建类似的通用地图时,您必须接受在检索值时必须进行向下转换。

EDIT3: Is there a way to fix this? EDIT3: 有没有办法解决这个问题? Of course, defining a function with signature make(Object... keysAndValues) would work, but I would loose the compile time typesafety. 当然,使用签名make(Object ... keysAndValues)定义一个函数会起作用,但是我会放弃编译时类型安全。

You will always loose compile time safety at a certain point. 在某个时刻,您总是会失去编译时的安全性。 At least when it comes to retrieval. 至少在检索方面。

This does compile: 这确实编译:

Map<Integer, ? extends Serializable> map = make(1, "bla", 3, 17);

This is what Java's generic type inference is giving you. 这就是Java的通用类型推断给你的。 Not what you expected? 不是你所期望的?

You have a couple of options, either use a clarifier on the method: 你有几个选择,要么在方法上使用澄清器:

Map<Integer, Object> map = Util.<Integer, Object>make(1, "bla", 3, 17);

Or you can use objects that don't have a common parent interface. 或者您可以使用没有公共父接口的对象。

Map<Integer, Object> map = make(1, "bla", 3, new Object());

The compiler has to "guess" the types KEY and VALUE from the parameters given (the types of your Map where you store the return value are ignored). 编译器必须从给定的参数中“猜测” KEYVALUE类型(忽略存储返回值的Map的类型)。 The "guess" for VALUE is the strictest type fitting to both Integer and String: anything that implements Comparable and Serializable ( Object&Serializable&Comparable<?> ). VALUE的“猜测”是适合Integer和String的最严格类型:实现Comparable和Serializable( Object&Serializable&Comparable<?> )的任何东西。

You have two options: casting at least one VALUE to Object: 您有两个选择:将至少一个VALUE转换为Object:

Map<Integer, Object> map = make(1, (Object)"bla", 3, 17);

or using a wildcard in your map 或在地图中使用通配符

Map<Integer, ?> map = make(1, "bla", 3, 17);

String实际上实现了SerializableComparable接口,而Object也没有..所以它说的对象没有实现,但它的含义类型字符串确实...

The following solution looks like it should work, but strangely it does only work in the Eclipse compiler, but not on a plain Java 6 compiler. 以下解决方案看起来应该可以工作,但奇怪的是它只能在Eclipse编译器中工作,而不能在普通的Java 6编译器上工作。 8-{ Why??? 8- {为什么???

The method declaration is annoyingly complicated but allows the simplest syntax when creating maps: 方法声明非常复杂,但在创建地图时允许最简单的语法:

public static <KEY, AKEY extends KEY, VALUE, AVALUE extends VALUE> Map<KEY, VALUE> make2(final AKEY key1,
        final AVALUE value1,
        final AKEY key2,
        final AVALUE value2)

Here, the compiler infers from the context in 这里,编译器从上下文中推断出来

Map<Integer, Object> map = make(1, "bla", 3, 17);

that KEY should be Integer and VALUE should be Object; KEY应为Integer,VALUE应为Object; AKEY is inferred from the given parameters as Integer and AVALUE as Object&Serializable&Comparable , and the compiler can verify that AKEY conforms to KEY and AVALUE to VALUE. AKEY从给定参数推断为Integer,AVALUE推断为Object&Serializable&Comparable,编译器可以验证AKEY是否符合KEY,AVALUE符合VALUE。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM