双通配符通用类型错误

时间:2015-04-22 19:17:38

标签: java generics

我正在尝试为我的自定义++类型定义一个运算符Map,如下所示:

@Override
public MutableMap<K, V> $plus$plus(Map<? extends K, ? extends V> map)
{
    HashMap<K, V> copy = this.copy();
    map.$plus$plus$eq(map);
    return copy;
}

++=运算符的定义如下:

public void $plus$plus$eq(Map<? extends K, ? extends V> map);

然而,编译器在map.$plus$plus$eq(map);行上抱怨,出现以下疯狂错误:

The method $plus$plus$eq(Map<? extends capture#10-of ? extends K,
? extends capture#11-of ? extends V>) in the type
Map<capture#10-of ? extends K,capture#11-of ? extends V> is not
applicable for the arguments
(Map<capture#12-of ? extends K,capture#13-of ? extends V>)

正如您在此屏幕截图中看到的那样,Eclipse提供的解决方案都不起作用,但仅此一项甚至是有意义的:

Eclipse Marker

我现在已经使用Java泛型了很长一段时间,甚至为自定义编程开发了我自己的通用类型系统(我目前正在尝试编写的库),但我之前从未遇到过这样的错误。

编辑 :有趣的是,将map参数转换为原始类型(Map)似乎可以解决问题。

map.$plus$plus$eq((Map) map);

但是,将强制转换更改为(Map<?, ?>)(这是Eclipse的第二个解决方案所做的)会导致类似的错误。

2 个答案:

答案 0 :(得分:4)

如果您使用原始类型Map,它会起作用,但您会失去类型安全性。不要使用原始类型。它们只存在与Java 1.4及更旧版本的兼容性,它们没有泛型。

它不适用于通配符,因为编译器不知道通配符?的确切类型。原因与您不能call add() on a List<? extends T>的问题相同。

请注意,通配符意味着您可以使用键的类型扩展K的任何类型的对象,并且值的类型扩展为V。相反,它意味着您有一个映射,其中键是某些特定的,但未知类型K,并且值是某些特定的,但未知类型V。你不能在这样的地图上调用$plus$plus$eq因为编译器不知道确切的类型,所以它无法检查它们。

原则上,您调用方法的Map的通配符可能代表与您作为参数传递的Map的通配符不同的类型 - 即使您可以在这种情况下,看它们必须是相同的,因为在这种情况下你使用相同的对象map

您可以使用类型参数而不是通配符来修复它:

@Override
public <KK extends K, VV extends V> MutableMap<K, V> $plus$plus(Map<KK, VV> map)
{
    HashMap<K, V> copy = this.copy();
    map.$plus$plus$eq(map);
    return copy;
}

答案 1 :(得分:2)

这是一个也没有编译的简化示例。

class Foo<T> {

    void bar(Foo<? extends T> foo) {
        foo.bar(foo);
    }
}

假设foogoo的类型为Foo<? extends T>。这意味着foo的类型为Foo<U>,其中UT的子类型,goo的类型为Foo<V>,其中V也是T foo.bar(goo)的子类型。您不希望V起作用,因为U可能不是foo.bar(foo)的子类型。出于这个原因,foo也没有编译。这看起来很疯狂,因为foopublic void CancleSeat(String[][] SC, String can, int c, int r, int Seats, int an){ FillArray(SC); if (can.equals("No")){ System.out.println("OK, no cancelations for you!"); } else if (can.equals("Yes")){ System.out.println("How many seats would you like to cancel?"); an = scanner.nextInt(); for (int i = 0; i < an; i++){ System.out.println("What row is your seat in that you would like to cancle?"); r = scanner.nextInt(); r = r - 1; System.out.println("What colum is your seat in that you would like to cancle?"); c = scanner.nextInt(); c = c -1; if (SC[r][c].equals("-")){ System.out.println("That seat isn't researved pleas pick a different seat"); System.out.println("What row is your seat in that you would like to cancle?"); r = scanner.nextInt(); r = r - 1; System.out.println("What colum is your seat in that you would like to cancle?"); c = scanner.nextInt(); c = c - 1; } else if (SC[r][c].equals("X") || SC[r][c].equals("x")){ SC[r][c] = "-"; } } } } 是相同的实例,但参数是否适用仅取决于它们的编译时类型。