Я не могу понять, где final
ключевое слово действительно удобно, когда оно используется в параметрах метода.
Если мы исключим использование анонимных классов, читаемость и декларацию намерений, то это кажется мне почти бесполезным.
Обеспечение того, что некоторые данные остаются постоянными, не так сильно, как кажется.
Если параметр является примитивом, то он не будет иметь никакого эффекта, так как параметр передается методу в качестве значения, и его изменение не будет иметь никакого эффекта вне области действия.
Если мы передаем параметр по ссылке, то сама ссылка является локальной переменной, и если ссылка изменяется внутри метода, это не будет иметь никакого эффекта за пределами области действия метода.
Рассмотрим простой пример теста ниже. Этот тест проходит, хотя метод изменил значение заданной ссылки, но не имеет никакого эффекта.
public void testNullify() {
Collection<Integer> c = new ArrayList<Integer>();
nullify(c);
assertNotNull(c);
final Collection<Integer> c1 = c;
assertTrue(c1.equals(c));
change(c);
assertTrue(c1.equals(c));
}
private void change(Collection<Integer> c) {
c = new ArrayList<Integer>();
}
public void nullify(Collection<?> t) {
t = null;
}
источник
int foo(int* bar)
последнимint foo(int* &bar)
. Последний передает указатель по ссылке, первый передает ссылку по значению.Ответы:
Остановить переназначение переменной
Хотя эти ответы интеллектуально интересны, я не прочитал короткий простой ответ:
Независимо от того, является ли переменная статической переменной, переменной-членом, локальной переменной или переменной аргумента / параметра, эффект полностью одинаков.
пример
Давайте посмотрим эффект в действии.
Рассмотрим этот простой метод, в котором обеим переменным ( arg и x ) могут быть назначены разные объекты.
Пометьте локальную переменную как окончательную . Это приводит к ошибке компилятора.
Вместо этого давайте пометим переменную параметра как окончательную . Это также приводит к ошибке компилятора.
Мораль истории:
Никогда не переназначать аргументы
Как хорошая практика программирования (на любом языке), вы никогда не должны повторно назначать переменную параметра / аргумента объекту, отличному от объекта, переданного вызывающим методом. В приведенных выше примерах никогда не следует писать строку
arg =
. Поскольку люди делают ошибки, а программисты - люди, давайте попросим компилятор помочь нам. Пометьте каждую переменную параметра / аргумента как 'final', чтобы компилятор мог найти и пометить любое такое переназначение.Ретроспективно
Как отмечалось в других ответах… Учитывая первоначальную цель разработки Java, заключающуюся в том, чтобы помочь программистам избежать глупых ошибок, таких как чтение за концом массива, Java должна была быть спроектирована так, чтобы автоматически приводить в действие все переменные параметра / аргумента как «final». Другими словами, Аргументы не должны быть переменными . Но задним числом является видение 20/20, и дизайнеры Java были заняты в то время.
Итак, всегда добавлять
final
ко всем аргументам?Должны ли мы добавить
final
к каждому объявленному параметру метода?➥ Добавляйте
final
только тогда, когда код метода длинный или сложный, где аргумент может быть принят за локальную переменную или переменную-член и, возможно, переназначен.Если вы согласны с тем, что никогда не будете повторно назначать аргумент, вы будете склонны добавлять
final
к каждому из них. Но это утомительно и затрудняет чтение декларации.Для короткого простого кода, где аргумент, очевидно, является аргументом, а не локальной переменной или переменной-членом, я не беспокоюсь о добавлении
final
. Если код достаточно очевиден, и у меня нет шансов, что ни я, ни какой-либо другой программист не выполнят обслуживание или рефакторинг, случайно приняв переменную аргумента за что-то отличное от аргумента, тогда не беспокойтесь. В моей собственной работе я добавляюfinal
только более длинный или более сложный код, где аргумент может быть принят за локальную переменную или переменную-член.Еще один случай, добавленный для полноты
источник
message = ( msg || 'Hello World"' )
. Просто нет причин не использовать отдельную переменную. Единственная стоимость - несколько байтов памяти.message = ( msg || 'Hello World"' )
риск, я позже случайно используюmsg
. Когда контракт, который я намереваюсь, звучит так: «поведение с аргументом no / null / undefined arg неотличимо от прохождения"Hello World"
», хорошей практикой программирования является принятие его в начале функции. [Это может быть достигнуто без переназначения, начиная с,if (!msg) return myfunc("Hello World");
но это становится громоздким с несколькими аргументами.] В тех редких случаях, когда логика в функции должна заботиться о том, использовалось ли значение по умолчанию, я бы предпочел назначить специальное значение для стража (предпочтительно общедоступное) ,message
иmsg
. Но если бы он назвал это чем-то похожимprocessedMsg
или что-то еще, что обеспечивает дополнительный контекст - вероятность ошибки намного ниже. Сосредоточьтесь на том, что он говорит, а не на том, «как» он это говорит. ;)Иногда приятно явно (для удобства чтения), что переменная не меняется. Вот простой пример, где использование
final
может спасти некоторые возможные головные боли:Если вы забыли ключевое слово this в установщике, то переменная, которую вы хотите установить, не будет установлена. Однако если вы используете
final
ключевое слово в параметре, то ошибка будет обнаружена во время компиляции.источник
Да, исключая анонимные классы, читаемость и декларацию намерений, это почти бесполезно. Эти три вещи бесполезны, хотя?
Лично я склонен не использовать
final
локальные переменные и параметры, если я не использую переменную в анонимном внутреннем классе, но я, безусловно, вижу смысл тех, кто хочет прояснить, что само значение параметра не изменится (даже если объект, на который он ссылается, изменяет свое содержимое). Для тех, кто считает, что это улучшает читабельность, я думаю, что это вполне разумно.Ваша точка будет более важно , если кто - нибудь на самом деле были утверждая , что это было держать постоянные данные таким образом , что он не делает - но я не могу вспомнить , видя такие претензии. Вы предполагаете, что есть значительная часть разработчиков, предполагающих, что
final
имеет больший эффект, чем на самом деле?РЕДАКТИРОВАТЬ: Я действительно должен был подвести итог всего этого с ссылкой на Монти Пайтон; вопрос кажется чем-то похожим на вопрос: «Что римляне когда-либо делали для нас?»
источник
Позвольте мне объяснить немного об одном случае, когда у вас есть использовать final, о котором Джон уже упоминал:
Если вы создаете анонимный внутренний класс в своем методе и используете локальную переменную (например, параметр метода) внутри этого класса, то компилятор заставит вас сделать параметр окончательным:
Здесь
from
иto
параметры должны быть окончательными , так что они могут быть использованы внутри анонимного класса.Причина этого требования заключается в следующем: локальные переменные живут в стеке, поэтому они существуют только во время выполнения метода. Однако экземпляр анонимного класса возвращается из метода, поэтому он может жить намного дольше. Вы не можете сохранить стек, потому что он необходим для последующих вызовов методов.
Поэтому вместо этого Java помещает копии этих локальных переменных в качестве скрытых переменных экземпляра в анонимный класс (вы можете увидеть их, если изучите байт-код). Но если они не были окончательными, можно ожидать, что анонимный класс и метод увидят изменения, которые другой вносит в переменную. Чтобы сохранить иллюзию, что существует только одна переменная, а не две копии, она должна быть окончательной.
источник
final
параметром функции и параметром нефинала в глазах HotSpot?Я использую final все время по параметрам.
Это добавляет так много? На самом деле, нет.
Буду ли я выключить это? Нет.
Причина: я обнаружил 3 ошибки, когда люди написали небрежный код и не смогли установить переменную-член в методах доступа. Все ошибки оказалось трудно найти.
Я хотел бы видеть это сделанным по умолчанию в будущей версии Java. Передача по значению / справке запутывает огромное количество начинающих программистов.
Еще одна вещь ... мои методы, как правило, имеют небольшое количество параметров, поэтому дополнительный текст в объявлении метода не является проблемой.
источник
Использование final в параметре метода не имеет ничего общего с тем, что происходит с аргументом на стороне вызывающей стороны. Он предназначен только для того, чтобы пометить его как не изменяющийся внутри этого метода. Поскольку я пытаюсь принять более функциональный стиль программирования, я как бы вижу ценность в этом.
источник
final
параметры в интерфейсах / объявлениях абстрактных методов.Лично я не использую final для параметров метода, потому что это добавляет слишком много беспорядка в списки параметров. Я предпочитаю следить за тем, чтобы параметры метода не изменялись через что-то вроде Checkstyle.
Для локальных переменных я использую final когда бы ни было возможно, я даже позволил Eclipse сделать это автоматически в моей настройке для личных проектов.
Я, конечно, хотел бы что-то более сильное, как C / C ++ const.
источник
Поскольку Java передает копии аргументов, я чувствую, что актуальность
final
довольно ограничена. Я полагаю, что эта привычка возникла в эпоху C ++, когда вы могли запретить изменение ссылочного содержимого, выполнив aconst char const *
. Я чувствую, что такого рода вещи заставляют вас верить, что разработчик по своей сути глуп, как f ***, и должен быть защищен от действительно каждого символа, который он печатает. При всей скромности могу сказать, что я пишу очень мало ошибок, даже если я их опускаюfinal
(если я не хочу, чтобы кто-то переопределял мои методы и классы). Может быть, я просто разработчик старой школы.источник
Я никогда не использую final в списке параметров, он просто добавляет беспорядок, как говорили предыдущие респонденты. Также в Eclipse вы можете установить назначение параметров для генерации ошибки, поэтому использование final в списке параметров мне кажется излишним. Интересно, что когда я включил настройку Eclipse для назначения параметров, генерирующую ошибку, он поймал этот код (это только то, как я помню поток, а не фактический код.): -
Играя адвоката дьявола, что именно не так с этим делать?
источник
Краткий ответ:
final
немного помогает, но ... вместо этого используйте защитное программирование на стороне клиента.В самом деле, проблема в
final
том, что он только гарантирует, что ссылка неизменна, и радостно разрешает мутировать ссылочные элементы объекта без уведомления вызывающей стороны. Следовательно, наилучшей практикой в этом отношении является защитное программирование на стороне вызывающего, создание глубоко неизменных экземпляров или глубоких копий объектов, которые могут быть ограблены недобросовестными API.источник
Еще одна причина для добавления final к объявлениям параметров заключается в том, что он помогает идентифицировать переменные, которые должны быть переименованы как часть рефакторинга «Extract Method». Я обнаружил, что добавление final к каждому параметру перед запуском рефакторинга большого метода быстро говорит мне, есть ли какие-либо проблемы, которые мне нужно решить перед продолжением.
Однако я обычно удаляю их как лишние в конце рефакторинга.
источник
Следите за тем, что написал Мишель. Я сам сделал другой пример, чтобы объяснить это. Я надеюсь, что это может помочь.
Из кода выше, в методе thisIsWhy () , мы на самом деле не назначали в [аргументе MyObj OBJ] к реальной ссылке в MyParam. Вместо этого мы просто используем [аргумент MyObj obj] в методе внутри MyParam.
Но после того, как мы закончим метод thisIsWhy () , должен ли существовать аргумент (объект) MyObj?
Похоже, что так и должно быть, потому что мы можем видеть в main, что мы все еще вызываем метод showObjName (), и ему нужно достичь obj . MyParam по-прежнему использует / достигает аргумента метода, даже если метод уже был возвращен!
Как Java на самом деле этого добивается, так это генерация копии - это скрытая ссылка на аргумент MyObj obj внутри объекта MyParam (но это не формальное поле в MyParam, поэтому мы его не видим)
Поскольку мы называем "showObjName", он будет использовать эту ссылку для получения соответствующего значения.
Но если мы не поставили аргумент final, что приводит к ситуации, мы можем переназначить новую память (объект) на аргумент MyObj obj .
Технически нет никакого столкновения! Если нам позволят это сделать, ниже будет ситуация:
Никакого столкновения, но "ОГРОМНОЕ !!" Потому что все они используют одно и то же «ссылочное имя», которое «obj» .
Чтобы избежать этого, установите его как «окончательный», чтобы программист не делал «подверженный ошибкам» код.
источник