почему мы должны использовать final для обозначения параметра конструктора в перечислении? [Дубликат]

Вы установили плагин Advanced Java Folding , который делает именно то, что вы видите, сбрасывает и аббревиатура некоторых языковых конструкций.

Либо удалить / отключить подключаемый модуль или настроить его варианты в соответствии с вашими личными предпочтениями:

320
задан Basil Bourque 5 September 2013 в 23:14
поделиться

12 ответов

Остановить переназначение переменной

Хотя эти ответы интеллектуально интересны, я не прочел короткий простой ответ:

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

Является ли переменная статической переменной, переменной-членом, локальной переменной или аргументом / параметром

Пример

Давайте посмотрим на эффект в действии.

Рассмотрим этот простой метод, где две переменные ( arg и x ) оба могут быть повторно назначены разные объекты.

// Example use of this method: 
//   this.doSomething( "tiger" );
void doSomething( String arg ) {
  String x = arg;   // Both variables now point to the same String object.
  x = "elephant";   // This variable now points to a different String object.
  arg = "giraffe";  // Ditto. Now neither variable points to the original passed String.
}

Отметьте локальную переменную как final . Это приводит к ошибке компилятора.

void doSomething( String arg ) {
  final String x = arg;  // Mark variable as 'final'.
  x = "elephant";  // Compiler error: The final local variable x cannot be assigned. 
  arg = "giraffe";  
}

Вместо этого отметим параметрную переменную как final . Это также приводит к ошибке компилятора.

void doSomething( final String arg ) {  // Mark argument as 'final'.
  String x = arg;   
  x = "elephant"; 
  arg = "giraffe";  // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
}

Мораль истории:

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

Никогда не переназначайте аргументы

Как хорошая практика программирования (на любом языке), вы должны никогда повторно назначить переменную параметра / аргумента объекту, отличному от объекта, переданного вызывающим методом. В приведенных выше примерах никогда не следует писать строку arg =. Поскольку люди делают ошибки, а программисты - люди, давайте попросим компилятор помочь нам. Пометьте каждую переменную параметра / аргумента как «final», чтобы компилятор мог найти и помечать любые такие перераспределения.

В Retrospect

Как отмечено в других ответах ... Учитывая оригинальный дизайн Java цель помочь программистам избежать немых ошибок, таких как чтение за концом массива, Java должен был быть разработан для автоматического принудительного применения всех переменных параметра / аргумента как «final». Другими словами, Аргументы не должны быть переменными . Но задним числом является 20/20 видение, и разработчики Java в свое время были в полной мере.

В дополнение к полноте

public class MyClass {
    private int x;
    //getters and setters
}

void doSomething( final MyClass arg ) {  // Mark argument as 'final'.

   arg =  new MyClass();  // Compiler error: The passed argument variable arg  cannot be re-assigned to another object.

   arg.setX(20); // allowed
  // We can re-assign properties of argument which is marked as final
 }
193
ответ дан Basil Bourque 22 August 2018 в 20:20
поделиться
  • 1
    «Как хорошая практика программирования (на любом языке), вы никогда не должны переназначать переменную параметра / аргумента [..] & quot; Извините, мне действительно нужно позвонить вам. Повторное назначение аргументов является стандартной практикой в ​​таких языках, как Javascript, где количество переданных аргументов (или даже если они есть) не продиктовано сигнатурой метода. Например. с учетом такой подписи, как: "function say (msg)" люди будут уверены, что аргумент «msg» назначен так: «msg = msg || 'Hello World!'; & Quot ;. Лучшие программисты Javascript в мире нарушают вашу хорошую практику. Просто прочитайте источник jQuery. – Stijn de Witt 27 November 2013 в 10:07
  • 2
    @StijndeWitt Ваш пример показывает саму проблему переназначения переменной аргумента. Вы теряете информацию с помощью ничего не получили взамен: (a) Вы потеряли исходное значение, переданное, (b) Вы потеряли намерение метода вызова (Пропустил ли вызывающий вызов «Привет, мир!» или мы по умолчанию). И & amp; b полезны для тестирования, длинного кода, а также после дальнейшего изменения значения. Я согласен с моим утверждением: arg vars следует переназначить never . Ваш код должен быть: message = ( msg || 'Hello World"' ). Просто нет причины not использовать отдельный var. Единственная стоимость - это несколько байтов памяти. – Basil Bourque 27 November 2013 в 23:07
  • 3
    @Basil: Это больше кода (в байтах) и в Javascript, который действительно учитывает. Крупный. Как и во многих вещах, это основано на мнениях. Вполне возможно полностью игнорировать эту практику программирования и писать отличный код. Практика программирования одного человека не делает ее обычной практикой. Встаньте в это все, что пожелаете, я все равно буду писать. Это делает меня плохим программистом или кодом кода? – Stijn de Witt 10 April 2014 в 13:40
  • 4
    Используя message = ( msg || 'Hello World"' ), я рискую позже, используя msg. Когда контракт, который я намереваюсь, имеет «поведение без аргумента / null / undefined arg, является неотличимым от передачи "Hello World"», хорошая практика программирования заключается в том, чтобы зафиксировать его в начале функции. [Это может быть достигнуто без переназначения, начиная с if (!msg) return myfunc("Hello World");, но это становится громоздким с несколькими аргументами.] В редких случаях, когда логика в функции должна заботиться о том, использовалась ли по умолчанию, я бы скорее обозначил специальное значение дозорного ( предпочтительно публично). – Beni Cherniavsky-Paskin 22 March 2015 в 13:44
  • 5
    @ BeniCherniavsky-Paskin, который вы описываете, зависит только от сходства между message и msg. Но если он назвал бы это что-то вроде processedMsg или что-то еще, что обеспечит дополнительный контекст - вероятность ошибки намного ниже. Сосредоточьтесь на том, что он говорит not on & quot; как & quot; он это говорит. ;) – alfasin 13 May 2016 в 22:10

Короткий ответ: final помогает маленькому биту, но ... вместо этого используйте защитное программирование на стороне клиента.

Действительно, проблема с final в том, что она только обеспечивает, чтобы ссылка не изменялась , радостно позволяя связанным объектам-объектам быть мутированными, без ведома вызывающего. Следовательно, наилучшей практикой в ​​этом отношении является защитное программирование со стороны вызывающего абонента, создающее глубоко неизменные экземпляры или глубокие копии объектов, которым грозит опасность недобросовестных API.

1
ответ дан Darrell Teague 22 August 2018 в 20:20
поделиться
  • 1
    "проблема с final заключается в том, что она только обеспечивает, чтобы ссылка не изменялась & quot; - Неисправность, сама Java предотвращает это. Переменная, переданная в метод, не может изменить ссылку на этот метод. – Madbreaks 11 April 2018 в 01:06
  • 2
    Пожалуйста, исследуйте перед публикацией ... stackoverflow.com/questions/40480/… – Darrell Teague 12 April 2018 в 15:31
  • 3
    Проще говоря, если бы правда, что ссылки на ссылки не могут быть изменены, не было бы обсуждений оборонительного копирования, неизменности, отсутствия необходимости в ключевом ключе и т. Д. – Darrell Teague 12 April 2018 в 15:32
  • 4
    Либо ты меня не понимаешь, либо ошибаешься. Если я передаю ссылку на объект методу, и этот метод переназначает его, исходная ссылка остается неповрежденной для (меня) вызывающей стороны, когда метод завершает свое выполнение. Java строго передается по значению. И вы нагло надуманны, чтобы утверждать, что я не проводил никаких исследований. – Madbreaks 12 April 2018 в 16:39
  • 5
    Downvoting, потому что op спросил, зачем использовать final, и вы дали только одну неправильную причину. – Madbreaks 12 April 2018 в 16:46

Я использую окончательное все время по параметрам.

Значит ли это так много? Не совсем.

Могу ли я отключить его? Нет.

Причина: я обнаружил 3 ошибки, когда люди написали неаккуратный код и не смогли установить переменную-член в accessors. Все ошибки оказались труднодоступными.

Хотелось бы, чтобы это сделало дефолт в будущей версии Java. Передача по значению / справочной вещи вызывает множество программистов младшего возраста.

Еще одна вещь. Мои методы имеют низкое количество параметров, поэтому дополнительный текст в объявлении метода не является проблемой.

25
ответ дан Fortyrunner 22 August 2018 в 20:20
поделиться
  • 1
    Я тоже собирался предложить это окончательное значение в будущих версиях, и вы должны указать «изменчивый», или лучшего ключевого слова, которое задумано. Вот хорошая статья об этом: lpar.ath0.com/2008/08/26/java-annoyance-final-parameters – Jeff Axelrod 11 May 2011 в 17:39
  • 2
    Это было давно, но не могли бы вы привести пример ошибки, которую вы поймали? – user949300 27 September 2017 в 06:05
  • 3
    См. Наиболее проголосовавший за ответ. Это очень хороший пример, когда переменная-член не задана, а параметр мутирован. – Fortyrunner 27 September 2017 в 22:21

Использование final в параметре метода не имеет ничего общего с тем, что происходит с аргументом на стороне вызывающего абонента. Это означает, что это не изменяется внутри этого метода. Когда я пытаюсь принять более функциональный стиль программирования, я как бы вижу значение в этом.

18
ответ дан Germán 22 August 2018 в 20:20
поделиться
  • 1
    Точно, это не часть функционального интерфейса, а только реализация. Смешно, что java разрешает (но dirsregards) final параметры в описаниях интерфейсов / абстрактных методов. – Beni Cherniavsky-Paskin 22 March 2015 в 13:53

Иногда приятно быть явным (для удобочитаемости), что переменная не изменяется. Вот простой пример, когда использование final может сэкономить некоторые возможные головные боли

public void setTest(String test) {
    test = test;
}

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

204
ответ дан Jerry Sha 22 August 2018 в 20:20
поделиться
  • 1
    btw вы увидите предупреждение & quot; Назначение переменной теста не имеет никакого эффекта & quot; так или иначе – AvrDragon 4 April 2012 в 17:03
  • 2
    @AvrDragon Но мы также можем игнорировать это предупреждение. Таким образом, всегда лучше иметь что-то, что помешает нам идти дальше, как ошибка компиляции, которую мы получим, используя ключевое слово final. – Sumit Desai 30 April 2013 в 10:49
  • 3
    @AvrDragon Это зависит от среды разработки. Вы не должны полагаться на IDE, чтобы поймать такие вещи для вас в любом случае, если вы не хотите развивать вредные привычки. – b1nary.atr0phy 28 May 2013 в 10:11
  • 4
    @ b1naryatr0phy на самом деле это предупреждение компилятора, а не только IDE-tip – AvrDragon 28 May 2013 в 10:18
  • 5
    @SumitDesai "Но мы также можем игнорировать предупреждение. Таким образом, всегда лучше иметь что-то, что помешает нам идти дальше, как ошибка компиляции, которую мы получим, используя последнее ключевое слово. & Quot; Я считаю, что это очень сильное утверждение, которое, по моему мнению, не согласуется с многими разработчиками Java. Предупреждения компилятора существуют по какой-то причине, и компетентный разработчик не должен ошибаться, чтобы «заставить» их рассмотреть его последствия. – Stuart Rossiter 11 November 2015 в 14:35

Да, исключая анонимные классы, читаемость и декларирование намерений, это почти бесполезно. Эти три вещи бесполезны?

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

Ваш вопрос был бы более важным, если бы кто-то действительно утверждал, что он сделал , чтобы данные были постоянными таким образом, что это не так, - но я не могу вспомнить, что видел такие претензии. Вы предполагаете, что существует значительное количество разработчиков, предлагающих, что final обладает большим эффектом, чем это действительно делает?

EDIT: Я действительно должен был бы суммировать все это с помощью ссылки Monty Python; вопрос кажется несколько похожим на вопрос: «Что римляне когда-либо делали для нас?»

123
ответ дан Jon Skeet 22 August 2018 в 20:20
поделиться
  • 1
    Ну ... что римляне сделали для нас? – matt b 1 February 2009 в 18:09
  • 2
    Но перефразировать Krusty своим датским, что они сделали для нас LATELY ? знак равно – James Schek 5 February 2009 в 16:37
  • 3
    Юваль. Забавно! Я думаю, что мир может произойти, даже если он будет применен мечом! – gonzobrains 2 August 2012 в 18:55
  • 4
    Вопрос кажется более похожим на вопрос: «Что не римляне сделали для нас?», Потому что это скорее критика о том, что последнее ключевое слово не сделай. – NobleUplift 14 October 2013 в 16:40
  • 5
    @SarthakMittal: значение не будет скопировано, если вы на самом деле его не используете, если это то, что вам интересно. – Jon Skeet 18 September 2017 в 07:32

Я никогда не использую final в списке параметров, он просто добавляет беспорядок, как говорили предыдущие респонденты. Также в Eclipse вы можете установить назначение параметров для генерации ошибки, поэтому использование final в списке параметров кажется мне излишним. Интересно, когда я включил параметр Eclipse для назначения параметров, генерируя ошибку, он поймал этот код (это как раз то, как я помню поток, а не фактический код.): -

private String getString(String A, int i, String B, String C)
{
    if (i > 0)
        A += B;

    if (i > 100)
        A += C;

    return A;
}

Играя адвоката дьявола, что именно не так с этим?

0
ответ дан Kellen Donohue 22 August 2018 в 20:20
поделиться
  • 1
    Будьте осторожны, чтобы отличить IDE от JVM времени выполнения. Независимо от того, что делает IDE, не имеет значения, если скомпилированный байт-код выполняется на сервере, если только IDE не добавил код для защиты от зависания элемента-члена, например, из-за ошибки в коде, когда предполагалось, что переменная не должна быть переназначена, но ошибочно была - следовательно, цель ключевое слово final. – Darrell Teague 13 August 2018 в 17:52

Следуйте за сообщением Мишеля. Я сам сделал себе еще один пример, чтобы объяснить это. Я надеюсь, что это могло бы помочь.

public static void main(String[] args){
    MyParam myParam = thisIsWhy(new MyObj());
    myParam.setArgNewName();

    System.out.println(myParam.showObjName());
}

public static MyParam thisIsWhy(final MyObj obj){
    MyParam myParam = new MyParam() {
        @Override
        public void setArgNewName() {
            obj.name = "afterSet";
        }

        @Override
        public String showObjName(){
            return obj.name;
        }
    };

    return myParam;
}

public static class MyObj{
    String name = "beforeSet";
    public MyObj() {
    }
}

public abstract static class MyParam{
    public abstract void setArgNewName();
    public abstract String showObjName();
}

Из приведенного выше кода в методе thisIsWhy () мы фактически не присвоили [аргумент MyObj obj] реальную ссылку в MyParam. Вместо этого мы просто используем [аргумент MyObj obj] в методе внутри MyParam.

Но после завершения метода thisIsWhy (), должен существовать аргумент (объект) MyObj?

Похоже, должно быть, потому что мы можем видеть в основном, мы все еще вызываем метод showObjName (), и ему нужно достичь obj. MyParam все еще использует / достигает аргумента метода, даже если метод уже возвращался!

Как Java действительно достигает этого - сгенерировать копию, также является скрытой ссылкой аргумента MyObj obj внутри объекта MyParam (но это не формальное поле в MyParam, так что мы не можем его увидеть)

Как мы называем «showObjName», он будет использовать эту ссылку для получения соответствующего значения.

Но если мы не представим аргумент final, что приведет к ситуации, мы можем переназначить новая память (объект) к аргументу MyObj obj.

Технически нет никакого столкновения! Если нам будет позволено это сделать, ниже будет ситуация:

  1. Теперь у нас есть скрытая точка [MyObj obj] к [Память A в куче], теперь живущая в объекте MyParam.
  2. У нас также есть еще один [MyObj obj], который является аргументом, указывающим на [Память B в куче], теперь живущим в этом методе IsWhy.

Нет столкновения, но «CONFUSING! !» Потому что все они используют одно и то же «ссылочное имя», которое является «obj».

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

-1
ответ дан KunYu Tsai 22 August 2018 в 20:20
поделиться

Поскольку Java передает копии аргументов, я считаю, что ее значимость весьма ограничена. Я предполагаю, что это происходит из эпохи C ++, где вы можете запретить ссылочный контент для изменения, выполнив const char const *. Я чувствую, что такие вещи заставляют вас полагать, что разработчику присуще глупо, как f ***, и его нужно защищать от поистине каждого персонажа, которого он набирает. Во всех смирениях я должен сказать, что я пишу очень мало ошибок, хотя я опускаю final, если только я не хочу, чтобы кто-то переоценивал мои методы и прочее ... Может быть, я просто девчонка старой школы ... : -O

3
ответ дан MC Emperor 22 August 2018 в 20:20
поделиться

Позвольте мне немного рассказать об одном случае, когда вы использовали для использования final, о котором уже упоминал Джон:

Если вы создаете анонимный внутренний класс в своем методе и используете локальная переменная (такая как параметр метода) внутри этого класса, тогда компилятор заставляет вас сделать параметр final:

public Iterator<Integer> createIntegerIterator(final int from, final int to)
{
    return new Iterator<Integer>(){
        int index = from;
        public Integer next()
        {
            return index++;
        }
        public boolean hasNext()
        {
            return index <= to;
        }
        // remove method omitted
    };
}

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

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

Таким образом, вместо того, чтобы вместо Java использовать копии этих локальных переменных в качестве скрытых переменных экземпляра в анонимный класс, вы можете увидеть их (если вы посмотрите на байтовый код). Но если они не были окончательными, можно было бы ожидать, что анонимный класс и метод, видя изменения, другой, внесенный в переменную. Чтобы сохранить иллюзию, что есть только одна переменная, а не две копии, она должна быть окончательной.

74
ответ дан Michael Borgwardt 22 August 2018 в 20:20
поделиться
  • 1
    Вы потеряли меня от «Но если они не окончательные ...» Можете ли вы попытаться перефразировать его, возможно, у меня не было достаточного количества cofee. – hhafez 2 February 2009 в 06:45
  • 2
    У вас есть локальные переменные из - вопрос заключается в том, что произойдет, если вы используете экземпляр класса anon внутри метода и он изменит значения from - люди ожидали бы, что изменение будет видимым в методе, поскольку они видят только одну переменную. Чтобы избежать этой путаницы, она должна быть окончательной. – Michael Borgwardt 2 February 2009 в 11:40
  • 3
    Он не делает копию, это просто ссылка на любой объект. – vickirk 10 February 2010 в 14:40
  • 4
    @vickirk: уверен, что он делает копию - ссылки, в случае ссылочных типов. – Michael Borgwardt 10 February 2010 в 15:12
  • 5
    Btw, предполагая, что у нас нет анонимных классов, ссылающихся на эти переменные, знаете ли вы, существует ли какая-либо разница между параметром функции final и не конечным параметром функции в глазах HotSpot? – Pacerier 17 November 2011 в 14:34

Еще одна причина добавления окончательных объявлений в параметры состоит в том, что он помогает идентифицировать переменные, которые необходимо переименовать как часть рефакторинга «Метод извлечения». Я обнаружил, что добавление окончательного значения для каждого параметра перед началом рефакторинга большого метода быстро говорит мне, есть ли какие-либо проблемы, которые мне нужно решить, прежде чем продолжить.

Однако я обычно удаляю их как лишние в конце рефакторинг.

0
ответ дан Michael Rutherfurd 22 August 2018 в 20:20
поделиться

Лично я не использую final в параметрах метода, потому что он добавляет слишком много беспорядка в списки параметров. Я предпочитаю, чтобы эти параметры метода не менялись с помощью чего-то вроде Checkstyle.

Для локальных переменных я использую final по возможности, даже позволяю Eclipse делать это автоматически в моей настройке для личных проектов.

Мне бы, конечно, понравилось нечто вроде C / C ++ const.

5
ответ дан starblue 22 August 2018 в 20:20
поделиться
  • 1
    Неверные ссылки на IDE и инструменты применимы к публикации OP или теме. А именно «окончательный» это проверка времени компиляции, чтобы ссылка не была изменена / отлажена. Кроме того, чтобы действительно обеспечить соблюдение таких вещей, см. Ответ об отсутствии защиты для детей-членов окончательных ссылок. Например, при создании API, использование IDE или инструмента не поможет внешним сторонам, использующим / расширяющим такой код. – Darrell Teague 5 December 2016 в 18:34
Другие вопросы по тегам:

Похожие вопросы: