Дженерики Java кодируют компиляции с javac, сбои с Eclipse Helios

У меня есть следующий тестовый класс, который использует дженерики для перегрузки метода. Это работает при компиляции с javac и сбоями для компиляции в Eclipse Helios. Моя версия Java 1.6.0_21.

Все статьи, которые я прочитал, указывают, что Eclipse является правильным, и этот код не должен работать. Однако при компиляции с javac и выполнении правильный метод выбран.

Как это возможно?

Спасибо!

import java.util.ArrayList;

public class Test {
    public static void main (String [] args) {
        Test t = new Test();
        ArrayList<String> ss = new ArrayList<String>();
        ss.add("hello");
        ss.add("world");
        ArrayList<Integer> is = new ArrayList<Integer>();
        is.add(1);
        is.add(2);
        System.out.println(t.getFirst(ss));
        System.out.println(t.getFirst(is));
    }   
    public String getFirst (ArrayList<String> ss) {
        return ss.get(0);
    }
    public Integer getFirst (ArrayList<Integer> ss) {
        return ss.get(0);
    }
}
9
задан BalusC 10 August 2010 в 20:53
поделиться

7 ответов

У меня работает в Eclipse Helios. Выбор метода происходит во время компиляции, и у компилятора достаточно информации для этого.

3
ответ дан 4 December 2019 в 11:39
поделиться

Этот код корректен, как описано в JLS 15.12.2.5 Выбор наиболее специфичного метода.

Также рассмотрим кодирование в интерфейс:

List<String> ss = new ArrayList<String>();
List<Integer> is = new ArrayList<Integer>();
// etc.

Как отмечает @McDowell, измененные сигнатуры методов появляются в файле класса:

$ javap build/classes/Test
Compiled from "Test.java"
public class Test extends java.lang.Object{
    public Test();
    public static void main(java.lang.String[]);
    public java.lang.String getFirst(java.util.ArrayList);
    public java.lang.Integer getFirst(java.util.ArrayList);
}

Обратите внимание, что это не противоречит замечанию @meriton о файле класса. Например, вывод этого фрагмента

Method[] methods = Test.class.getDeclaredMethods();
for (Method m : methods) {
    System.out.println(Arrays.toString(m.getGenericParameterTypes()));
}

показывает формальный параметр main(), а также два параметра общего типа:

[class [Ljava.lang.String;]
[java.util.ArrayList<java.lang.String>]
[java.util.ArrayList<java.lang.Integer>]
4
ответ дан 4 December 2019 в 11:39
поделиться

Вы уверены, что Eclipse также настроен на использование Java 1.6?

2
ответ дан 4 December 2019 в 11:39
поделиться

Это было бы возможно, если бы в javac была ошибка. Javac - это просто программное обеспечение, и оно так же подвержено ошибкам, как и любое другое программное обеспечение.

Кроме того, спецификация языка Java очень сложна и местами немного расплывчата, так что это также может быть связано с различием в интерпретации между разработчиками Eclipse и разработчиками javac.

Я бы начал с того, что спросил об этом на каналах поддержки Eclipse. Обычно они очень хорошо улавливают эти вещи и объясняют, почему они думают, что они правы, или же признают, что они неправы.

Для протокола, я думаю, что Eclipse тоже здесь.

2
ответ дан 4 December 2019 в 11:39
поделиться

Спецификация языка Java, раздел 8.4.2 пишет:

Объявление двух методов с помощью заменяющие эквивалентные подписи (определено ниже) в классе.

Две сигнатуры метода m1 и m2 эквивалентны замещению, если либо m1 является дополнительной подписью m2, либо m2 является дополнительной подписью m1.

Подпись метода m1 является дополнительной подписью метода m2, если либо

  • m2 имеет ту же подпись, что и m1, либо

  • подпись метода m1 такая же, как стирание подписи метода м2.

Очевидно, что методы не эквивалентны замещению, поскольку ArrayList не является ArrayList (стирание ArrayList ).

Итак, объявление методов законно. Кроме того, выражение вызова метода является допустимым, поскольку тривиально существует наиболее специфичный метод , поскольку существует только один метод, соответствующий типам аргументов.

Править : Ишай правильно указывает, что в этом случае есть еще одно ограничение, которое сильно обходит стороной. Спецификация языка Java, раздел 8.4.8.3 пишет:

Это ошибка времени компиляции, если объявление типа T имеет метод-член m1 и существует метод m2 объявленный в T или супертип T такой что все следующие условия удерживайте:

  • m1 и m2 имеют одно и то же имя.
  • m2 доступен из T.
  • Подпись m1 не является дополнительной подписью (§8.4.2) подписи m2.
  • m1 или какой-либо переопределяемый метод m1 (прямо или косвенно) имеет то же стирание, что и m2 или какой-либо переопределяемый метод m2 (прямо или косвенно).

Приложение: О достоверности и ее отсутствии

Вопреки распространенному мнению, обобщения в сигнатурах методов не стираются. Обобщения стираются в байт-коде (набор команд виртуальной машины Java). Сигнатуры методов не являются частью набора инструкций; они записываются в файл класса, как указано в исходном коде. (Кстати, эту информацию также можно запросить во время выполнения с помощью отражения).

Подумайте об этом: если параметры типа были полностью удалены из файлов классов, как могло завершение кода в IDE по вашему выбору отображать, что ArrayList.add (E) принимает параметр типа E , а не Object (= удаление E ), если у вас не был прикреплен исходный код JDK? И как компилятор узнает об ошибке компиляции, если статический тип аргумента метода не является подтипом E ?

6
ответ дан 4 December 2019 в 11:39
поделиться

Eclipse и javac используют разные компиляторы. Eclipse использует сторонний компилятор для преобразования вашего кода в байт-коды для виртуальной машины Java. Javac использует компилятор Java, чем это публикует Sun. Поэтому возможно, что идентичный код дает несколько разные результаты. Я считаю, что Netbeans использует компилятор Sun, так что проверьте его и там.

0
ответ дан 4 December 2019 в 11:39
поделиться

Следует иметь в виду, что (все? Конечно, некоторые, например, редактор NetBeans это делает) инструменты статического анализа кода не учитывают тип возвращаемого значения или модификаторы (частный / общедоступный и т. Д.) ) как часть сигнатуры метода.

Если это так, то с помощью стирания типа оба метода getFirst получат подпись getFirst (java.util.ArrayList) и, следовательно, вызовут конфликт имен. ..

0
ответ дан 4 December 2019 в 11:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: