2

我已经知道 Java 基于编译时类型调度方法。但是我有一个案例,我希望它可以工作,但事实并非如此。

考虑这个简单的例子:

class Foo{ 
    void bar(Object... objects) { //do something }

    void bar(Map<String, Object> map) { //do something else }
}

和调用代码:

Foo foo = new Foo();
HashMap<String, T> map = createSomeHashMap(); //wil generate HashMap
foo.bar(map);

为什么 Java 认为调用它是最合适的bar(Object... objects)?因为我在编译时有一个地图,所以一切都应该工作!为什么我必须像这样明确地贬低它foo.bar((Map<String, Object>)map);

4

1 回答 1

2

我在下面尝试了这个程序并得到了一个错误Type mismatch: T cannot be converted to Object

public class DispatchTest
{

   private void bar( HashMap<String, Object> map )
   {
   }

   public static void main( String[] args )
   {
      test();
   }

   private static <T> void test()
   {
      DispatchTest dt = new DispatchTest();
      HashMap<String,T> map = new HashMap<>();
      dt.bar( map );
   }
}

所以我想是泛型把你搞砸了。将参数的类型从 更改Object?,这对我有用。

private void bar( HashMap<String, ?> map )
{
}

编辑:只是为了详细说明这一点,我将代码恢复为原始代码,并添加了一个类似于您的示例的方法bar(Object...)。下面是生成的 Java 字节码:

private static <T extends java/lang/Object> void test();
Code:
   0: new           #3                  // class quicktest/DispatchTest
   3: dup
   4: invokespecial #4                  // Method "<init>":()V
   7: astore_0
   8: new           #5                  // class java/util/HashMap
  11: dup
  12: invokespecial #6                  // Method java/util/HashMap."<init>":()V
  15: astore_1
  16: aload_0
  17: iconst_1
  18: anewarray     #7                  // class java/lang/Object
  21: dup
  22: iconst_0
  23: aload_1
  24: aastore
  25: invokevirtual #8                  // Method bar:([Ljava/lang/Object;)V

  28: return

您可以看到,在这种情况下,已经做出了Map参数不合适的决定,并且第 25 行的 invokevirtual 字节码Object...需要调用的版本。这是在编译时完成的,而不是运行时。

如果我将代码改回我的建议(使用Map<String,?>),则 invokevirual 字节码会要求提供 Map 参数。您的版本也可以工作,因为强制转换强制编译器为 Map 发出 invokevirtual,而不是 Object ......它通常会解析为。

于 2014-07-23T18:16:50.967 回答