Нарушена диспетчеризация параметров во время компиляции Java?

Я уже знаю, что 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)? Так как у меня есть карта в compileTime, все должно работать! Почему я должен явно понизить это как foo.bar((Map<String, Object>)map);??

1 ответ

Решение

Я попробовал эту программу ниже и получил ошибку 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 параметр не подходит, и байт-код invokevirtual в строке 25 хочет Object... версия звонка. Это делается во время компиляции, а не во время выполнения.

Если я изменю код обратно на мое предложение (используя Map<String,?>), тогда байт-код invokevirual запрашивает параметр Map. Ваша версия тоже работает, потому что приведение заставляет компилятор выдавать invokevirtual для карты, а не для объекта... который обычно разрешается.

Другие вопросы по тегам