В чем разница между каждым и традиционным для цикла в Java с точки зрения затенения?
Это код, который у меня есть:
class HelloWorld {
char[] foo = {'a', 'b'};
// This will compile
void foo() {
for (char foo : foo) {
}
}
// This will not compile
void bar() {
for (char foo = 0; foo < foo.length; foo++) {
}
}
}
Как получилось, что foo
компилирует, но компилирует bar
с:
Error: char cannot be dereferenced
В чем разница между двумя объявлениями цикла, которые делают цикл в компиляции foo, но бар не работает?
Ответы
Ответ 1
Мы можем видеть разницу, глядя на JLS§14.14.2 описание того, как улучшенный for
работает при обработке массива:
Усиленный оператор for эквивалентен основному выражению формы:
T[] #a = Expression;
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) {
{VariableModifier} TargetType Identifier = #a[#i];
Statement
}
Обратите внимание, как переменная объявляется внутри тела цикла, а не заголовка цикла. То есть ваша функция foo
выглядит следующим образом:
void foo() {
{ // Freestanding block for scope, though not really needed as `foo` has
// nothing else in it
char[] a = foo; // T[] #a = Expression;
for (int i = 0; i < a.length; i++) {
char foo = a[i]; // {VariableModifier} TargetType Identifier = #a[#i];
}
}
}
Вот почему вы избегаете теневого копирования в расширенном for
, а не в традиционном for
, которому необходимо получить доступ к исходному массиву (чтобы получить его length
, чтобы получить запись для i
и т.д.).
Подробнее о расширенном цикле for
в Как работает цикл Java для каждого цикла? и его ответы.
Ответ 2
Спецификация языка Java определяет область действия двух char foo
по-разному:
Область локальной переменной, объявленной в части ForInit базовой for
(§14.14.1) включает в себя все следующее:
- Свой собственный инициализатор
- Любые другие деклараторы справа в части ForInit оператора
for
- Элементы выражения и ForUpdate оператора
for
- Заявление
Область локальной переменной, объявленной в части FormalParameter расширенный оператор for
(§14.14.2) - это содержащееся выражение.
(JLS 8, раздел 6.3)
Это прекрасно объясняет ваше наблюдаемое поведение: локальный foo
находится в области действия в элементе управления основного цикла for
(везде справа от его объявления), и поэтому он тенерирует другой foo
там, но в расширенном цикле for
его область действия является только оператором цикла - она не находится в области где-либо в предложении управления циклом.