какая польза от ключевого слова final? - PullRequest
11 голосов
/ 29 декабря 2010

В приведенном ниже коде, если я удаляю ключевое слово 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);
            }
        });

Ответы [ 7 ]

21 голосов
/ 29 декабря 2010

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

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

15 голосов
/ 29 декабря 2010

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

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

3 голосов
/ 29 декабря 2010

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

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

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, а не «значение» 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. : -)

2 голосов
/ 14 января 2015

Назначение ключевого слова «final» в JAVA можно определить на трех уровнях: класс, метод, переменная

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.
2 голосов
/ 29 декабря 2010

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

0 голосов
/ 30 декабря 2010

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

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

Если тип не является окончательным, как компилятор определит, как построить реализацию внутреннего класса.По сути, объявляя поле final, вы даете компилятору 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 и декомпилируя этот файл байт-кода.

0 голосов
/ 29 декабря 2010

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

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

...