Приведение во внутренний класс с помощью дженериков - PullRequest
6 голосов
/ 11 ноября 2010

Рассмотрим следующий код:

public class Outer<T>  {

    public class Inner{
    }

    public static <T> Outer<T>.Inner get(){
        Object o = new Object();
        return (Outer<T>.Inner)o;
    }

    public static void main(String[] args) throws Exception {
        Outer.<String>get();
    }
}

Этот код успешно компилируется в Eclipse, но не компилируется в javac:

Outer.java:10: ')' expected
        return (Outer<T>.Inner)o;
                        ^
Outer.java:10: ';' expected
        return (Outer<T>.Inner)o;
                         ^
Outer.java:10: illegal start of expression
        return (Outer<T>.Inner)o;
                              ^
3 errors

Это ошибка в javacили Eclipse?

Если я изменю приведение на (Outer.Inner)o, оно компилируется, хотя есть предупреждение:

Затмение:

Outer.Inner is a raw type. References to generic type Outer<T>.Inner should be parameterized

javac:

Outer.java:10: warning: [unchecked] unchecked conversion
found   : Outer.Inner
required: Outer<T>.Inner
        return (Outer.Inner)o;
               ^
1 warning

Версия Javac: 1.6.0_21

Ответы [ 8 ]

4 голосов
/ 14 января 2011

Я обнаружил, что это ошибка в компиляторе javac, которая с тех пор была исправлена.JDK 7b100 компилирует этот штраф.

См. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6665356

3 голосов
/ 11 ноября 2010

Самое забавное в том, что, если нет ничего, что я упускаю в дженериках Java, оба

return (Outer<T>.Inner) o;

И

return (Outer.Inner) o;

Оба компилируются в один и тот же байт-код.

Проблема с первой строкой возникает при разборе - это означает, что javac и Eclipse не используют один и тот же парсер для исходного кода Java.Вам следует задать вопрос о том, какие различия существуют между java-анализатором Eclipse JDT и javac.(Или отправьте сообщение об ошибке в Eclipse).

Если вы настаиваете на сохранении этого поведения (я бы предложил рефакторинг Inner для статического внутреннего класса), вы можете использовать @SuppressWarning с назначением поля (дляограничьте @SuppressWarning наименьшей возможной областью).

@SuppressWarnings({"rawtypes","unchecked"})
Outer<T>.Inner casted = (Outer.Inner)o;
return casted;

EDIT : ОК, я думаю, что получил - JDT Eclipse анализирует код Java перед передачей его компилятору - иих синтаксический анализатор может иметь смысл такого приведения, в то время как (по крайней мере, ваша и моя версия) javac нет.(И после этого Eclipse напрямую передает проанализированный код на компиляцию).Прежде чем сообщать об ошибке, посмотрите, как ведет себя последняя версия Java 6.

1 голос
/ 11 ноября 2010

К сожалению, если вы кастуете с Object, вы не сможете увернуться от неконтролируемого предупреждения.Это потому, что у Object недостаточно информации о типе, чтобы иметь T.

(Обобщения Java основаны на стирании. Поэтому нет способа узнать, имеет ли объект аргумент типа T во время выполнения --- аргументы типа используются только во время компиляции.)

0 голосов
/ 11 ноября 2010

(даю другой ответ, потому что мой предыдущий слишком шумный.)

Для компиляции исходного кода Java в байт-код необходимо сделать два больших шага:

  1. Исходный кодкод должен быть разобран в синтаксическое дерево.
  2. синтаксическое дерево используется для генерации байт-кода .

Когда javac компилируется, он использует свой собственный синтаксический анализатори генератор байт-кода (детали реализации которого будут зависеть от используемого вами JDK).

Когда JDT Eclipse компилируется, он использует свой собственный анализатор кода и после этого ... я не знаю.Я хочу подчеркнуть, что, так или иначе, они «обходят» часть синтаксического анализатора javac.(Например, они могут перейти к измененным javac-файлам java, которые заменяют все приведения универсальных классов необработанными классами).

Моя точка зрения - в конце концов, это ошибка в части javac, котораяразбирает исходный код Java(Нет причины, по которой такая конструкция не была бы разрешена в спецификации языка.)

Чтобы противодействовать этой ошибке, вы могли бы: * Изменить дизайн своего приложения, чтобы полностью его избежать.* Каждый раз, когда вам нужно сделать это приведение, каждый помещает необработанный тип и аннотацию @SuppressWarnings вместо естественного приведения.

0 голосов
/ 11 ноября 2010

Если вы должны использовать приведение, вы можете подавить предупреждение с помощью @ SuppressWarnings

   Object o = new Object();

   @SuppressWarnings("unchecked")
   Outer<T>.Inner returnVal = (Outer.Inner) o;

   return returnVal;

Но осознайте, что предупреждение существует, потому что вы делаете что-то небезопасное.Объект не может быть приведен к String.Это приведет к исключению во время выполнения.

И, как заметил The Elite Gentleman, вы можете пометить Inner как статический:

public static class Inner {
0 голосов
/ 11 ноября 2010

В вашем коде есть две проблемы:

Первая - во время компиляции: вы не можете безопасно привести объект к универсальному типу, так как обобщенные типы не реализованы в Java.Приведение к неинстанцированному дженерику явно опасно для вашего javac.Я боюсь, что ответ компилятора на это зависит от поставщика и версии компилятора.

Во-вторых, приведение Object типа Object к любому другому типу вызовет ClassCastException во время выполнения, поскольку в Java информация о типесодержится в объекте и не изменится после создания.

0 голосов
/ 11 ноября 2010

Следующее «исправление» работало, когда я компилировал с javac.Он также успешно скомпилирован в Eclipse.Проблема, которую я воспринимаю, заключается в том, что вы не можете создать новый объект из переменной (как, например, то, что вы сделали в вашем случае).Я не знаю, как это объяснить или подтвердить мою теорию.

/**
 * 
 */
package testcases;

/**
 * @author The Elite Gentleman
 *
 */
public class Outer<T> {

    public class Inner{
    }

    public static <T> Outer<T>.Inner get(){
        //Object o = new Object();
        //return (Outer<T>.Inner)o;
        return new Outer<T>().new Inner();
    }

    public static void main(String[] args) throws Exception {
        Outer.<String>get();
    }
}

По сути, поскольку Inner не является статическим вложенным классом, для его реализации, вот как вы это сделаете:

new Outer<T>().new Inner();

Кроме того, Object o = new Object(); не гарантирует, что объект o на самом деле является экземпляром типа Inner класса.


Обновление Мое решение помогло с созданием объекта, а не для приведения объекта к существующему объекту.Для этого у меня нет ответов (но мы разработчики, мы что-нибудь придумаем :-)).

Я могу подумать, почему бы не сделать класс Inner статическим вложенным классом?

0 голосов
/ 11 ноября 2010

Вы не можете наложить Object на что-то, что не является.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...