Каково использование ключевого слова final?

В приведенном ниже коде, если я удаляю ключевое слово final из EditText, я получаю ошибку в строке (6), где я передаю объект EditText (et) в намерение... Мне нужно определить значение конечного ключевого слова здесь...

final EditText et=(EditText)findViewById(R.id.t);
        Button b=(Button)findViewById(R.id.b1);
        b.setOnClickListener(new Button.OnClickListener(){
            public void onClick(View v)<br>
            {
            Intent on=new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+et.getText()));
            startActivity(on);
            }
        });

Ответ 1

Это потому, что вы используете закрытие здесь. Это означает, что внутренний класс использует контекст бесконечного. Чтобы использовать его, переменные должны быть объявлены окончательными, чтобы не меняться.

Подробнее здесь.

Ответ 2

Final по существу означает, что переменная et не будет переназначена ни в какой точке и останется вокруг. Это означает, что внутренние классы, такие как ваш слушатель, могут доверять тому, что он не будет переназначен каким-либо другим потоком, который может вызвать всевозможные проблемы.

final также может использоваться для изменения метода или определения класса, что означает, что этот метод не может быть переопределен подклассом или что класс не может быть расширен.

Ответ 3

Прочтите эту статью, чтобы понять детали реализации:

Причина этого ограничения становится очевидным, если мы проливаем некоторый свет о том, как реализуются локальные классы. Анонимный локальный класс может использовать локальный переменные, потому что компилятор автоматически дает классу a личное поле экземпляра для хранения копии каждой локальной переменной, используемой классом. Компилятор также добавляет скрытые параметров каждому конструктору инициализировать эти автоматически созданные частные поля. Таким образом, локальный класс на самом деле не переменные, а просто собственные их копии. Единственный способ, которым это может правильно работать, если переменные объявляются окончательными, так что они гарантированно не изменятся. При наличии этой гарантии локальный класс уверен, что его внутренние копии переменных точно отражают фактические локальные переменные.

EDIT:

Берлин Браун говорит: "Я опубликовал декомпилированную версию анонимный внутренний класс. Но быть честный, я до сих пор не понимаю, почему компилятор должен иметь эту информацию. Даже если поле объявлено окончательным, поле все равно может быть нулевым. я думаю это один из тех Java-причуд, вы должны объявить это поле окончательный... потому что так оно и есть. Нет ясной причины, по которой"

Причина заключается в том, чтобы убедиться, что пользователи понимают, что закрывает "близкие" переменные, а не значения. Предположим, что не было необходимости иметь конечные локальные переменные. Тогда мы могли бы написать код, например:

public void doIt() {
    for(int i = 0; i < 3; ++i) {
        runnables.add(new Runnable() {
            @Override
            public void run() {
                System.out.println(i);
            }
        });
    }
    run(runnables); // run each runnable
}

Как вы думаете, будет ли выход? Если вы думаете, что это будет "0 1 2", вы ошибаетесь, так как Runnable закрывает "переменную" i, а не "значение" я в этот момент времени, и поэтому выход будет "2 2 2". Что можно сделать для достижения ожидаемого поведения здесь? Два решения: либо полагаться на пользователей, чтобы они понимали, как работают замыкания, либо каким-то образом применяют его на уровне языка. И это второй вариант, с которым устроились разработчики языка.

public void doIt() {
    for(int i = 0; i < 3; ++i) {
        final int j = i; // notice the final local variable
        runnables.add(new Runnable() {
            @Override
            public void run() {
                System.out.println(j);
            }
        });
    }
    run(runnables);
}

JFTR, я не говорю, что второй вариант - это "способ", просто, если локальные переменные, помеченные как final, прежде чем использоваться в анонимных внутренних классах, для меня является большим нарушением. Конечно, YMMV.: -)

Ответ 4

Финал делает переменную et, разрешенную только один раз. Он также изменяет область действия переменной и позволяет функции onClick видеть et. Без финала et не отображается внутри функции onClick.

Ответ 5

Цель "окончательного" ключевого слова в JAVA может быть определена на трех уровнях: Class, Method, variable

Java final variable: If you make any variable as final, you cannot change the value of final variable (It will be constant).

Java final method: If you make any method as final, you cannot override it.

Java final class: If you make any class as final, you cannot extend it.

Ответ 6

Вот ссылка, в которой обсуждаются ключевые слова final. В соответствии с спецификацией (раздел 4.12.4):

Переменная может быть объявлена ​​окончательной. Конечная переменная может быть назначена только один раз. Это ошибка времени компиляции, если назначена конечная переменная, если она определенно не назначена (§16) непосредственно перед назначением.

Ответ 7

Это также помогает понять, как Java создает анонимный внутренний класс. Как Java реализует этот конкретный класс.

Java требует, чтобы эта переменная была окончательной, потому что компилятор должен знать тип этого объекта во время компиляции. Затем компилятор сгенерирует поле внутри анонимной внутренней реализации класса (в данном случае "et" ).

Если тип не является окончательным, как компилятор определит, как построить внутреннюю реализацию класса. В основном, объявив финал поля, вы даете компилятору Java больше информации.

Код, который не помогает компилятору, не будет компилировать ваш анонимный внутренний класс:

Object et;
et = a ? new Object() : new EditText();

...

С помощью вышеприведенного кода компилятор Java не может построить анонимный внутренний класс.

Ваш код:

final EditText et = (EditText) findViewById (R.id.t);

...

new Button.OnClickListener$1(){
            public void onClick(View v)<br>
            {
            Intent on=new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+et.getText()));
            startActivity(on);
            }
        });

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

public class OnClickListener$1 {

 private final EditText et ; <---- this is important
 public OnClickListener$1(final et) {
    this.et = et;
 }
 public void onClick(View v)<br>
                {
                Intent on=new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+et.getText()));
                startActivity(on);
                }
}

Вы можете протестировать псевдо-код, который я предоставил, найдя анонимный класс байт-кода $1 и декомпилировать этот файл байт-кода.