Где конечный параметр хранится в экземпляре анонимного класса?
У меня есть следующий статический метод factory, который создает представление списка из массива int:
public static List<Integer> newInstance(final int[] numbers) {
return new AbstractList<Integer>() {
@Override
public Integer get(int index) {
return numbers[index];
}
@Override
public int size() {
return numbers.length;
}
};
}
public static void main(String[] args) {
int[] sequence = {10, 20, 30};
List<Integer> list = ListFactory.newInstance(sequence);
System.out.println("List is "+list);
}
В "Эффективной Java" Джошуа Блох упомянул об этом
в качестве адаптера, который позволяет рассматривать массив int как список экземпляров Integer.
Однако я помню, что Adapter использует композицию, и экземпляр реализации анонимного списка должен использовать int [] как поле участника.
Где именно хранится входной параметр int [], если он не является полем члена реализации анонимного списка?
Я был бы признателен, если бы кто-нибудь мог предоставить некоторые идеи или некоторые ссылки для поиска дополнительной информации.
Ответы
Ответ 1
Вы можете использовать javac -d . -XD-printflat ListFactory.java
, чтобы узнать, как компилятор понимает внутренний класс. На самом деле в вашем примере есть два класса Java. ListFactory
(обратите внимание, как numbers
передается конструктору ListFactory$1
):
public class ListFactory {
public ListFactory() {
super();
}
public static List newInstance(final int[] numbers) {
return new ListFactory$1(numbers);
}
}
и представление анонимной реализации AbstractList
:
class ListFactory$1 extends AbstractList {
/*synthetic*/ final int[] val$numbers;
ListFactory$1(/*synthetic*/ final int[] val$numbers) {
this.val$numbers = val$numbers;
super();
}
@Override()
public Integer get(int index) {
return Integer.valueOf(val$numbers[index]);
}
@Override()
public int size() {
return val$numbers.length;
}
@Override()
/*synthetic*/ public Object get(/*synthetic*/ int index) {
return this.get(index);
}
}
Методы и поля, помеченные как синтетические, генерируются компилятором и недоступны для вас как программист, но используются во время выполнения для доступа к массиву int. И действительно есть поле val$numbers
, которое содержит окончательную ссылку на массив int.
Кстати, вы также можете заметить, что бокс от int
до Integer
в Integer get(int index)
и что для соответствия с необработанным (не общим) интерфейсом List
создается дополнительный Object get(int index)
метод, который делегирует реализация типа Integer get(int index)
.
Ответ 2
Он хранится внутри анонимного класса AbstractList
как синтетическое поле. Вы можете просмотреть его с помощью утилиты javap
:
final class q34290420.Test$1 extends java.util.AbstractList<java.lang.Integer> {
final int[] val$numbers; // here
q34290420.Test$1(int[]);
public java.lang.Integer get(int);
public int size();
public java.lang.Object get(int);
}
Кроме того, вы можете обнаружить это через отражение:
Field[] fields = list.getClass().getDeclaredFields();
System.out.println(fields[0].getName());
System.out.println(fields[0].isSynthetic());
Вывод:
val$numbers
true
Ответ 3
Это связано с вопросом: Почему только конечные переменные доступны в анонимном классе?
Джон Скит уже предоставляет краткий ответ для вопроса выше:
Когда вы создаете экземпляр анонимного внутреннего класса, любые переменные, которые используются в этом классе, имеют свои значения, скопированные с помощью автогенерированного конструктора. Это позволяет избежать компилятора, чтобы автогенерировать различные дополнительные типы для хранения логического состояния "локальных переменных".
Итак, в этом случае номера int [] автоматически копируются в анонимный класс, который простирается от AbstractList
как синтетическое поле.