Почему алмаз не может выводить типы на анонимные внутренние классы?
Возможный дубликат:
Инициализация двойной скобки (анонимный внутренний класс) с алмазным оператором
В Java 7 и более поздних версиях алмаз можно использовать для вывода типов, как обычно, без проблем:
List<String> list = new ArrayList<>();
Однако он не может для анонимных внутренних классов следующим образом:
List<String> st = new List<>() { //Doesn't compile
//Implementation here
}
Почему это? Логически в этом сценарии я могу определенно указать тип как String
. Есть ли логическая причина для этого решения, когда тип не может быть фактически выведен на анонимные внутренние классы, или он был опущен по другим причинам?
Ответы
Ответ 1
В JSR-334:
Использование алмаза с анонимными внутренними классами не поддерживается, поскольку для этого в общем случае потребуется расширение файла класса атрибут подписи для представления недентифицируемых типов, де-факто JVM изменение.
Я думаю, что, как известно, анонимный класс приводит к поколению собственного файла класса.
Я предполагаю, что общий тип не существует в этих файлах и скорее заменен эффективным (статическим) типом (таким образом, объявленным явным типом, таким как <String>
в момент времени объявления).
В самом деле, файл, соответствующий внутреннему классу, никогда не делится на несколько разных экземпляров, поэтому зачем беспокоиться о дженериках?:.)
Было бы более трудно достижимым (и, безусловно, бесполезным) для компилятора принудительное расширение (путем добавления специального атрибута для generics) к тезисов типа файлов классов.
Ответ 2
google yields, после пропуска сообщений из stackoverflow, http://mail.openjdk.java.net/pipermail/coin-dev/2011-June/003283.html
Я предполагаю, что это так, обычно анонимный класс представляет собой конкретный подкласс кажущегося типа
interface Foo<N extends Number>
{
void foo(N n);
}
Foo<Integer> foo = new Foo<Integer>(){ ... }
реализуется
class AnonFoo_1 implements Foo<Integer>{ ... }
Foo<Integer> foo = new AnonFoo_1();
Предположим, что мы разрешаем вывод диагноза на анонимных классах, может быть сложный случай типа
Foo<? extends Runnable> foo = new Foo<>(){ ... }
Правила вывода дают N=Number&Runnable
; после трюка, который нам нужен, нам нужно
class AnonFoo_2 implements Foo<Number&Runnable>{ ... }
В настоящее время это запрещено; тип arg to super type Foo
должен быть "нормальным" типом.
Однако обоснование не очень сильное. Мы можем придумать другие приемы реализации, чтобы заставить его работать.
class AnonFoo<N extends Number&Runnable> implements Foo<N>
{
@Override public void foo(N n)
{
n.intValue();
n.run();
}
}
Foo<? extends Runnable> foo = new AnonFoo<>();
компилятор должен иметь возможность сделать тот же трюк.
В любом случае, по крайней мере, компилятор должен разрешить большинство случаев использования, которые не включают "неопровержимые типы", например Foo<Integer> foo = new Foo<>(){...}
Жаль, что эти обычные/простые случаи также излишне запрещены.
Ответ 3
Короче говоря, <>
мало что делает для вывода типов, он отключает предупреждение, которое вы получите без него.
EDIT: как указывает @Natix, он выполняет некоторую проверку.
List<Integer> ints = new ArrayList<>();
List<String> copy = new ArrayList<>(ints);
создает ошибку компиляции
Error:Error:line (42)error: incompatible types
required: List<String>
found: ArrayList<Integer>
Как вы видите, <>
принимает тип аргумента, не вызывая тип из типа copy
Ответ 4
Интересные вопросы. Решение можно найти здесь http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.9 или здесь Двойная инициализация скобок (анонимный внутренний класс) с алмазный оператор
По крайней мере, я кое-что узнал. Спасибо за вопрос.;)