Нарушена диспетчеризация параметров во время компиляции 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 для карты, а не для объекта... который обычно разрешается.