Использование модификатора «final», когда это применимо в Java [закрыто]

194

В Java существует практика объявления каждой переменной (локальной или классовой), в качестве конечного параметра, если они действительно есть.

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

Что вы думаете об этом и что вы следуете?

surajs
источник
13
Это может привести к религиозному аргументу. Некоторым это нравится, некоторые ненавидят. Мне нравятся конечные поля, но не конечные локальные переменные, если они не должны быть, не уверен, что это полностью рационально. Не уверен, что любой отрицательный голос будет либо. Я согласен с Алексом Миллером. ;)
Питер Лори
Я могу понять, если людям не нравится, когда их код загроможден финалами. Но это проблема, которую может решить хороший редактор: bugs.eclipse.org/bugs/show_bug.cgi?id=409379
oberlies

Ответы:

184

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

Добавление finalко всем вещам, которые не должны изменяться, просто сужает возможности того, что вы (или следующий программист, работающий над вашим кодом) неверно истолковывают или неправильно используют мыслительный процесс, который привел к вашему коду. По крайней мере, он должен звонить в некоторые колокола, когда они теперь хотят изменить вашу ранее неизменную вещь.

Поначалу кажется немного неловко видеть много finalключевых слов в вашем коде, но довольно скоро вы перестанете замечать само слово и просто подумаете : «вещь никогда не изменится от этой точки» на (вы можете взять это от меня ;-)

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

Йохан Пелгрим
источник
16
Вместо использования последнего ключевого слова для параметров вы можете использовать инструмент статического анализа, например FindBugs, чтобы требовать, чтобы переменные параметров не переназначались. Таким образом вы снимаете синтаксическую нагрузку и можете применить ее с помощью проверки FindBugs в своем инструменте непрерывной интеграции.
Тимо Весткэмпер
20
@Timo Это бы сработало, но у него есть и обратная сторона: это нужно проверять после регистрации, а не во время работы разработчика над кодом. Ведь finalкомпилятор, очевидно, не позволит вам продолжить, если вы допустите ошибку.
Майк
9
В Eclipse есть возможность добавлять все finalвозможные значения (переменные, которые не меняются) при каждом сохранении.
Берт F
22
+1 люблю final. Это не имеет значения 98% времени, это незначительное неудобство% 1 времени, но 1% времени спасает меня от того, чтобы делать что-то глупое или непреднамеренное, оно того стоит.
Берт Ф
14
@BertF Да, я всегда позволяю Eclipse добавлять finalмодификаторы везде, когда я «очищаю ...» мой код. Везде означает даже в блоках catch (), и, как уже было сказано, вы не заметите их через некоторое время. Конечно, если бы я создать язык, то finalбыло бы по умолчанию, и varили modifiableбудет необязательным ключевым словом.
Maarten Bodewes
191

Одержимость над:

  • Финальные поля - Маркировка полей как финальных заставляет их устанавливать к концу построения, делая ссылку на это поле неизменной. Это позволяет безопасно публиковать поля и может избежать необходимости синхронизации при последующих чтениях. (Обратите внимание, что для ссылки на объект неизменна только ссылка на поле - вещи, на которые ссылается ссылка на объект, все еще могут изменяться, и это влияет на неизменность.)
  • Конечные статические поля - хотя я сейчас использую перечисления для многих случаев, когда я использовал статические конечные поля.

Рассмотрите, но используйте разумно:

  • Заключительные занятия - дизайн Framework / API - единственный случай, когда я рассматриваю это.
  • Финальные методы - в основном такие же, как финальные классы. Если вы используете шаблоны шаблонных методов, такие как сумасшедшие, и помечаете вещи как финальные, вы, вероятно, слишком полагаетесь на наследование и недостаточно на делегирование.

Проигнорируйте, если не чувствуете анальный

  • Параметры метода и локальные переменные - я редко делаю это в основном потому, что я ленив и нахожу, что это загромождает код. Я полностью признаю, что маркировка параметров и локальных переменных, которые я не собираюсь изменять, является «правильной». Я хотел бы, чтобы это было по умолчанию. Но это не так, и я нахожу код более сложным для понимания во всех финалах. Если я нахожусь в чужом коде, я не собираюсь их извлекать, но если я пишу новый код, я не буду их вставлять. Единственным исключением является случай, когда вам нужно пометить что-то окончательное, чтобы вы могли получить доступ это изнутри анонимного внутреннего класса.
Алекс Миллер
источник
8
лучший ответ на несколько вопросов на эту последнюю тему
Габриэль Шербак
4
Большую часть времени, когда я вижу локальную переменную без конечного слова перед ней и ее нельзя использовать там, это говорит мне, что мне, вероятно, следует извлечь некоторый код в новый метод, который возвращает желаемое значение, которое я сделаю окончательным. Случаи, когда это не применимо, это когда я использую некоторые потоки или нужно перехватить их.
Nyxz
32

Вы действительно должны понять полное использование окончательного ключевого слова перед его использованием. Он может применяться к переменным, полям, методам и классам и оказывать различное влияние

Я бы порекомендовал проверить статью, связанную с ниже, для более подробной информации.

Последнее слово о последнем ключевом слове

HowardSP
источник
29

finalМодификатор, особенно для переменных, является средством , чтобы компилятор соблюдения конвенции , которая , как правило , разумно: не убедитесь , что (локальный или экземпляр) переменная присваивается ровно один раз (не больше и не меньше). Убедившись, что переменная определенно назначена перед ее использованием, вы можете избежать распространенных случаев NullPointerException:

final FileInputStream in;
if(test)
  in = new FileInputStream("foo.txt");
else
  System.out.println("test failed");
in.read(); // Compiler error because variable 'in' might be unassigned

Предотвращая присвоение переменной более одного раза, вы препятствуете расширению границ. Вместо этого:

 String msg = null;
 for(int i = 0; i < 10; i++) {
     msg = "We are at position " + i;
     System.out.println(msg);
 }
 msg = null;

Вам предлагается использовать это:

 for(int i = 0; i < 10; i++) {
     final String msg = "We are at position " + i;
     System.out.println(msg);
 }

Некоторые ссылки:

Бруно Де Фрейн
источник
1
+1 за дискурсный аргумент в пользу широкой публики
Том Тресанский
Короче говоря, вы можете в основном использовать Javafinal для создания выражений .
Адам Гент
На самом деле «Убедившись, что переменная определенно назначена перед ее использованием, вы можете избежать распространенных случаев исключения NullPointerException:« это не правильно, «final» здесь не имеет значения, компилятор знает и, возможно, жалуется на переменную in » быть нулевым в приведенном примере.
Нихолку
@nyholku Вы правы, в Java локальные переменные должны быть обязательно назначены перед использованием, также без финала. Но для полей вам нужен финал. В немного более сложном примере, где «in» - это переменная экземпляра класса, а if / else находится в конструкторе, вам нужен финал, чтобы сигнализировать о проблеме. Кроме того, также для локальных переменных есть определенное значение в окончательном IMO, так как он не позволяет неаккуратному программисту «исправить» ошибку компиляции о том, что «in» может быть не назначено путем добавления «= null» в его объявление. Другими словами, последние переменные сокращают использование нуля, по моему опыту.
Бруно Де Фрейн
20

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

  1. Объявление намерения. Объявляя конечную переменную, я заявляю, что эта переменная предназначена для записи только один раз. Это тонкий совет другим разработчикам и большой совет компилятору.
  2. Применение одноразовых переменных: я верю в идею, что каждая переменная должна иметь только одну цель в жизни. Предоставляя каждой переменной только одну цель, вы сокращаете время, необходимое для достижения цели этой конкретной переменной при отладке.
  3. Позволяет оптимизировать: я знаю, что у компилятора были приемы повышения производительности, которые основывались на неизменности ссылки на переменную. Мне нравится думать, что некоторые из этих старых уловок производительности (или новых) будут использоваться компилятором.

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

Райан Рэнсфорд
источник
2
finalПеременные и параметры метода не влияют на производительность, поскольку они не выражены в байт-коде.
Стив Куо
переменная: = латинская: переменная> разная> изменяемая
Дитер
17

У эффективной Java есть пункт, который говорит: «Пользуйся неизменными объектами». Объявление полей как окончательных поможет вам сделать несколько небольших шагов в этом направлении, но, конечно, есть гораздо больше действительно неизменяемых объектов, чем это.

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

Ларс Вестергрен
источник
15
осторожно - окончательный и неизменный - совершенно разные понятия. Легко иметь конечный изменяемый объект - final касается ссылки, изменчивость касается экземпляра объекта. конечная персона olaf = новая персона (); olaf.setName ( "Олаф");
Олаф Кок
1
Точно - неизменные объекты - это одно, неизменяемые ссылки (то есть конечные) - это нечто совершенно другое.
SCdF
2
Но они тесно связаны, так как вы можете объявить поле Person.name как окончательное (и объявив класс final и ...) сделать объект Person окончательным. Это, однако, не так просто, как просто делать «последний человек» ...
Себастьян Гансланд
Это также не имеет значения для локальных переменных (параметры являются локальными). Это относится только к переменным класса, поэтому лишь частично решает вопрос.
Робин
12

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

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

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

Редактировать: В качестве пояснения (и попытки вернуть отрицательные голоса) я не говорю, не отмечайте константы как окончательные, я говорю, что не делайте такие вещи, как:

public String doSomething() {
  final String first = someReallyComplicatedExpressionToGetTheString();
  final String second = anotherReallyComplicatedExpressionToGetAnother();

  return first+second;
}

Это только делает код (на мой взгляд) труднее для чтения.

Также стоит помнить, что все , что нужно сделать в финале, - это помешать вам переназначить переменную, это не делает ее неизменной или что-то подобное.

SCdF
источник
1
Что касается заявления на голове: я был в многих ситуаций , в которых не используя final(и неизменные объекты в целом) были существенный фактор , способствующего число и влиянию ошибок.
Крис Вест
3
Я все для неизменных объектов, я просто никогда не был в ситуации, когда пометка окончательного неизменного объекта помогла мне.
SCdF
2
Я согласен, что мы не должны использовать final для локальных переменных и параметров методов, это делает код намного менее читабельным.
rmaruszewski
@ChrisVest, может быть, ваши функции слишком длинные
Pacerier
8

Финал всегда должен использоваться для констант. Это даже полезно для кратковременных переменных (в пределах одного метода), когда правила определения переменной сложны.

Например:

final int foo;
if (a)
    foo = 1;
else if (b)
    foo = 2;
else if (c)
    foo = 3;
if (d)        // Compile error:  forgot the 'else'
    foo = 4;
else
    foo = -1;
Дэвид Леппик
источник
6

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

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

RAY
источник
1
Downvoters, хотите объяснить?
Луч
Кажется странным называть это переменной, не так ли?
Алан
2
Есть тысячи английских слов на выбор.
RAY
Это потрясающе. Там нет аргументов против, finalкроме «слишком долго писать», «делает код неуклюжим». Есть несколько аргументов для final. И мы можем автоматически добавить его при сохранении, если вы не хотите вводить его вручную.
Дмитрий Попов
5

Я finalвсе время использую для атрибутов объекта.

finalКлючевое слово имеет видимость семантику при использовании атрибутов объекта. По сути, установка значения атрибута final происходит до того, как конструктор вернется. Это означает, что до тех пор, пока вы не дадите thisссылке покинуть конструктор и будете использовать ее finalдля всех своих атрибутов, ваш объект (в соответствии с семантикой Java 5) гарантированно будет правильно сконструирован, и, поскольку он также неизменен, его можно безопасно публиковать в другие темы.

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

Я иногда тоже делаю методы окончательными, но не так часто. Я редко делаю уроки окончательными. Я обычно делаю это, потому что мне мало нужно. Я, как правило, мало пользуюсь наследованием. Вместо этого я предпочитаю использовать интерфейсы и композицию объектов - это также поддается дизайну, который, как мне кажется, часто проще тестировать. Когда вы кодируете интерфейсы, а не конкретные классы, вам не нужно использовать наследование при тестировании, как это происходит с такими фреймворками, как jMock, гораздо проще создавать mock-объекты с интерфейсами, чем с конкретными классами.

Я думаю, что я должен сделать большинство своих уроков окончательными, но я просто еще не вошел в привычку.

Крис Вест
источник
4

Я должен прочитать много кода для своей работы. Отсутствие final для переменных экземпляра является одной из главных вещей, которые меня раздражают, и делает ненужным понимание кода. За мои деньги финал по локальным переменным вызывает больше беспорядка, чем ясности. Язык должен был быть разработан, чтобы сделать это по умолчанию, но мы должны жить с ошибкой. Иногда это полезно, особенно с циклами и определенным присваиванием с деревом if-else, но в основном это указывает на то, что ваш метод слишком сложен.

Том Хотин - Tackline
источник
Почему бы не использовать инструмент, который уменьшает беспорядок?
Пейсер
@Pacerier Как в редакторе, который искажает код?
Том Хотин -
3

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

В эффективной Java есть целый пункт (пункт 15), указывающий на ловушки непреднамеренного наследования. Фактически, если вы не спроектировали и не документировали свой класс для наследования, наследование от него может привести к непредвиденным проблемам (этот пример дает хороший пример). Поэтому рекомендуется использовать final для любого класса и / или метода, который не предназначен для наследования.

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

DJClayworth
источник
3

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

Uri
источник
И этот пост является большим доказательством этого.
InigoD
2

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

дислокация
источник
2

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

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

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

Олег Михеев
источник
1

Final при использовании с переменными в Java обеспечивает замену константы в C ++. Поэтому, когда final и static используются для переменной, она становится неизменной. В то же время, переносимые программисты на C ++ очень счастливы ;-)

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

Когда final используется с методом, он не позволяет переопределять метод подклассами.

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

Его следует использовать только для переменных, если вы абсолютно уверены, что значение переменной никогда не будет изменено. Также убедитесь, что вы соблюдаете соглашение о кодировании, поддерживаемое SUN.for, например: final int COLOR_RED = 1; (Верхний регистр отделен подчеркиванием)

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

Относительно читабельности, следует, что комментарии играют очень важную роль при использовании финального модификатора.

всемогущий
источник
Может кто-нибудь сказать мне, почему за это проголосовали ??? Просто любопытно ..
Всемогущий
Вероятно, потому, что вы заявляете, что должны делать ТОЛЬКО финал, когда XYZ. Другие считают, что лучше делать ВСЕ окончательным, если в этом нет необходимости.
Outlaw Programmer
1
Это ни в коем случае не заменяет C ++ const ключевое слово. -1.
tmj
1

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

То же самое относится и к переменным-членам, поскольку они дают мало преимуществ, за исключением случая констант.

Это также не имеет никакого отношения к неизменности, так как лучшим показателем того, что что-то является неизменным, является то, что оно задокументировано как таковое и / или не имеет методов, которые могут изменить объект (это, наряду с завершением класса, является единственным способом гарантировать, что это неизменно).

Но эй, это только мое мнение :-)

Робин
источник
1

Я настроил Eclipse, чтобы добавить final для всех полей и атрибутов, которые не были изменены. Это прекрасно работает, используя Eclipse «save actions», который добавляет эти финальные модификаторы (среди прочего) при сохранении файла.

Настоятельно рекомендуется.

Ознакомьтесь с моей записью в блоге Eclipse Save Actions.

zvikico
источник
1

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

Тот факт, что Eclipse окрашивает окончательный красный цвет, облегчает обнаружение объявлений переменных в коде, который, я думаю, в большинстве случаев улучшает читаемость.

Я пытаюсь обеспечить соблюдение правила о том, что все переменные должны быть окончательными, если нет веской причины не делать этого. Гораздо проще ответить на вопрос "что это за переменная?" вопрос, если вам просто нужно найти инициализацию и быть уверенным, что это так.

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

Финальная переменная - это просто хороший способ для обозначения значений.

Не финальная переменная связана с частью некоторого подверженного ошибкам алгоритма.

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

Джон Нильссон
источник
1

Я уже давно пишу код и использую final, когда могу. Сделав это некоторое время (для переменных, параметров метода и атрибутов класса), я могу сказать, что 90% (или больше) моих переменных на самом деле являются окончательными. Я думаю, что преимущество в том, что переменные НЕ модифицируются, когда вы этого не хотите (я видел это раньше, и иногда это бывает больно), оплачивая дополнительную типизацию и дополнительные «окончательные» ключевые слова в вашем коде.

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

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

Я также использую Collections.unmodifiable ... для создания неизменяемых списков, когда мне это нужно.

Рави Валлау
источник
0

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

Тем не менее, если вы обнаружите, что вам необходимо добавить много заключительных утверждений в ваш код. Это может быть хорошим намеком на то, что вы делаете что-то не так.

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

public void doSomething(int i, int j) {
    final int n = i + j; // must be declared final

    Comparator comp = new Comparator() {
        public int compare(Object left, Object right) {
            return n; // return copy of a local variable
        }
    };
}
Ханс Сьюннессон
источник
0

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

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

Что касается классов, только для некоторых классов инфраструктуры, я использовал последний класс.

IntelliJ IDEA предупреждает вас, если внутри функции записан параметр функции. Итак, я перестал использовать final для аргументов функции. Я не вижу их и внутри библиотеки Java Runtime.

anjanb
источник
0

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

В противном случае, я использую, наконец, только если это public/private static final type SOME_CONSTANT;

jjnguy
источник
Хм ... отредактировано в одном месте ... еще сказано, наконец, на второй строке ;-)
Всемогущий
Предоставление людям возможности переопределять ценности - это один из самых больших источников сюрпризов и сложных ошибок
RAY
0

Пометка класса final также может привести к привязке некоторых методов во время компиляции, а не во время выполнения. Рассмотрим «v2.foo ()» ниже - компилятор знает, что B не может иметь подкласс, поэтому foo () нельзя переопределить, поэтому вызываемая реализация известна во время компиляции. Если класс B НЕ помечен как финальный, то возможно, что фактическим типом v2 является некоторый класс, который расширяет B и переопределяет foo ().

class A {
    void foo() {
        //do something
    }
}
final class B extends A {
    void foo() {
    }
}
class Test {
    public void t(A v1, B v2) {
        v1.foo();
        v2.foo();
    }
}
Евгений
источник
-1

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

Павел Хайдан
источник
Это не усложняет тестирование, потому что вы все равно должны использовать интерфейсы.
Крис Вест