Когда следует использовать final для параметров метода и локальных переменных?
Я нашел пару ссылок (например), которые предлагают использовать final
как можно больше, и мне интересно, как это важно. Это главным образом в контексте параметров метода и локальных переменных, а не конечных методов или классов. Для констант это имеет очевидный смысл.
С одной стороны, компилятор может сделать некоторые оптимизации, и это делает программиста более четким. С другой стороны, это добавляет многословия, и оптимизации могут быть тривиальными.
Это что-то, что я должен сделать, чтобы помнить?
Ответы
Ответ 1
Одержимость:
- Заключительные поля. Пометка полей как окончательных заставит их быть установленными до конца построения, делая эту ссылку на поле неизменной. Это позволяет безопасно публиковать поля и может избежать необходимости синхронизации при последующих чтениях. (Обратите внимание, что для ссылки на объект только ссылка на поле неизменна - вещи, к которым ссылается ссылка объекта, все еще могут меняться и влияют на неизменность.)
- Конечные статические поля. Хотя я использую перечисления сейчас для многих случаев, когда я использовал статические конечные поля.
Подумайте, но используйте разумно:
- Заключительные классы - дизайн Framework/API - единственный случай, когда я его рассматриваю.
- Окончательные методы - в основном такие же, как и конечные классы. Если вы используете шаблонные методы шаблонов, такие как сумасшедшие и маркирующие вещи, вы, вероятно, слишком полагаетесь на наследование и недостаточно на делегирование.
Игнорируйте, если не почувствуете анальный эффект:
- Параметры метода и локальные переменные - Я РЕАЛЬНО сделаю это во многом потому, что я ленив, и я нахожу, что он загромождает код. Я полностью соглашусь с тем, что параметры маркировки и локальные переменные, которые я не собираюсь изменять, являются "более быстрыми". Мне жаль, что это не было по умолчанию. Но это не так, и я считаю, что код сложнее понять в финале. Если я нахожусь в чей-то еще код, я не собираюсь вытаскивать их, но если я пишу новый код, я не буду его вводить. Одно из исключений - это случай, когда вы должны отметить что-то окончательное, чтобы вы могли получить доступ это изнутри анонимного внутреннего класса.
Ответ 2
Это что-то, что я должен сделать, чтобы помнить?
Нет, если вы используете Eclipse, потому что вы можете настроить действие сохранения, чтобы автоматически добавлять эти окончательные модификаторы для вас. Затем вы получаете преимущества за меньшее усилие.
Ответ 3
Преимущества "окончательного" времени разработки не менее значительны, чем преимущества времени выполнения. Он рассказывает будущим редакторам кода о ваших намерениях.
Маркировка класса "final" означает, что вы не приложили усилий при разработке или реализации класса, чтобы грамотно обрабатывать расширение. Если читатели могут внести изменения в класс и хотят удалить "окончательный" модификатор, они могут сделать это на свой страх и риск. Это для них, чтобы убедиться, что класс будет хорошо работать с расширением.
Маркировка переменной "final" (и назначение ее в конструкторе) полезна при инъекции зависимостей. Это указывает на "соавтор" характер переменной.
Маркировка метода "final" полезна в абстрактных классах. Он четко определяет, где находятся точки расширения.
Ответ 4
Я нашел параметры меток маркировки и locals, поскольку final
полезен в качестве рефакторинга, когда рассматриваемый метод является непонятным беспорядком на несколько страниц. Погрузите final
либерально, посмотрите, что "не может назначить конечной переменной" ошибки, которые генерирует компилятор (или ваша IDE), и вы просто можете узнать, почему переменная с именем "данные" заканчивается нулевой, хотя несколько (устаревших) комментариев ругаться, что не может произойти.
Затем вы можете исправить некоторые ошибки, заменив повторно используемые переменные новыми переменными, объявленными ближе к точке использования. Затем вы обнаружите, что вы можете обернуть целые части метода в области фигурных скобок, и внезапно вы сделаете одно нажатие IDE вдали от "Extract Method", и ваш монстр просто станет более понятным.
Если ваш метод еще не является недостижимым крушением, я думаю, что может быть полезно сделать окончательный материал, чтобы препятствовать тому, чтобы люди превратили его в сказанное крушение; но если это короткий метод (см.: не недостижимый), то вы рискуете добавить много многословия. В частности, сигнатуры функций Java достаточно сложны, чтобы вписаться в 80 символов, так как это не добавляет еще шесть аргументов!
Ответ 5
Ну, это все зависит от вашего стиля... если вы LIKE видите финальный, когда вы не будете изменять эту переменную, а затем используйте ее. Если вы НЕ НРАВИТСЯ, увидев это... затем оставьте это.
Мне лично нравится как можно больше слов, поэтому я стараюсь избегать использования лишних ключевых слов, которые действительно не нужны.
Я предпочитаю динамические языки, хотя, наверное, не удивительно, что мне нравится избегать подробностей.
Итак, я бы сказал, просто выберите направление, на которое вы наклоняетесь, и просто пойдите с ним (независимо от того, попытайтесь быть последовательным).
В качестве побочного примечания я работал над проектами, которые используют и не используют такой шаблон, и я не видел разницы в количестве ошибок или ошибок... Я не думаю, что это шаблон это значительно улучшит ваш счетчик ошибок или что-то еще, но опять-таки это стиль, и если вам нравится выражать намерение, что вы его не модифицируете, тогда идите и используйте его.
Ответ 6
Если вы пишете приложение, которому кто-то должен будет прочитать код после, скажем, 1 год, то да, используйте final для переменной, которая не должна быть изменена все время. Делая это, ваш код будет более "самодокументирующимся", и вы также уменьшите вероятность того, что другие разработчики сделают глупые вещи, например, используя локальную константу в качестве локальной временной переменной.
Если вы пишете какой-то код, то, ну, не беспокойтесь, чтобы идентифицировать всю константу и сделать ее окончательной.
Ответ 7
Полезно в параметрах, чтобы случайно изменить значение параметра и ввести тонкую ошибку. Я использую, чтобы игнорировать эту рекомендацию, но потратив около 4 часов. в ужасном методе (с сотнями строк кода и нескольких fors, вложенными ifs и всякими плохими методами) я бы рекомендовал вам это сделать.
public int processSomethingCritical( final int x, final int y ){
// hundreds of lines here
// for loop here...
int x2 = 0;
x++; // bug aarrgg...
// hundreds of lines there
// if( x == 0 ) { ...
}
Конечно, в идеальном мире этого бы не случилось, но.. ну.. иногда вам приходится поддерживать другие коды.:(
Ответ 8
Я использую final
все время, чтобы сделать Java более основанным на выражениях. См. Условия Java (if,else,switch
) не являются выражениями, которые я всегда ненавидел, особенно если вы использовали функциональное программирование (например, ML, Scala или Lisp).
Таким образом, вы должны всегда стараться (IMHO) использовать конечные переменные при использовании условий.
Позвольте мне привести пример:
final String name;
switch(pluginType) {
case CANDIDATE_EXPORT:
name = "Candidate Stuff";
break;
case JOB_POSTING_IMPORT:
name = "Blah";
break;
default:
throw new IllegalStateException();
}
Теперь, если добавить другой оператор case
и не устанавливать name
, компилятор завершится с ошибкой. Компилятор также потерпит неудачу, если вы не сломаете каждый случай (который вы установите для переменной). Это позволяет сделать Java очень похожим на выражения Lisp let
и делает так, чтобы ваш код не был отступом (из-за лексических переменных).
И как заметила @Recurse (но, видимо, -1 меня), вы можете сделать предыдущее, не делая String name
final
, чтобы получить ошибку компилятора (о которой я никогда не говорил, что вы не могли), но вы можете легко сделать ошибка компилятора уходит из имени настройки после оператора switch, который отбрасывает семантику выражения или хуже забывает break
, из-за которой вы не можете вызвать ошибку (несмотря на то, что говорит @Recurse) без использования final
:
String name;
switch(pluginType) {
case CANDIDATE_EXPORT:
name = "Candidate Stuff";
//break; whoops forgot break..
//this will cause a compile error for final ;P @Recurse
case JOB_POSTING_IMPORT:
name = "Blah";
break;
}
// code, code, code
// Below is not possible with final
name = "Whoops bug";
Из-за имени настройки ошибки (кроме забывания break
, которое также является еще одной ошибкой), я могу случайно сделать это:
String name;
switch(pluginType) {
case CANDIDATE_EXPORT:
name = "Candidate Stuff";
break;
//should have handled all the cases for pluginType
}
// code, code, code
// Below is not possible with final
name = "Whoops bug";
Конечная переменная заставляет выполнить единую оценку того, какое имя должно быть. Подобно тому, как функция, которая имеет возвращаемое значение, всегда должна возвращать значение (игнорируя исключения), блок переключения имен должен будет разрешить имя и, следовательно, привязан к этому блоку коммутатора, который упрощает рефакторинг фрагментов кода (например, метод репликации Eclipe: extract).
Вышеописанное в OCaml:
type plugin = CandidateExport | JobPostingImport
let p = CandidateExport
let name = match p with
| CandidateExport -> "Candidate Stuff"
| JobPostingImport -> "Blah" ;;
match ... with ...
оценивается как функция, т.е. выражение. Обратите внимание, как выглядит наш оператор switch.
Вот пример в Схеме (Racket или Chicken):
(define name
(match b
['CandidateExport "Candidate Stuff"]
['JobPostingImport "Blah"]))
Ответ 9
Я буду использовать финал, насколько смогу. Это будет означать, если вы непреднамеренно измените поле. Я также задал параметры метода final. Сделав это, я обнаружил несколько ошибок из кода, который я использовал, когда они пытаются "установить" параметр, забывающий пропуски Java по значению.
Ответ 10
Не ясно из вопроса, очевидно ли это, но создание параметра параметра метода влияет только на тело метода. Он НЕ передает любую интересную информацию о намерениях метода для invoker. Передаваемый объект все еще может быть мутирован в рамках метода (финалы не являются константами), а область действия переменной находится внутри метода.
Чтобы ответить на ваш точный вопрос, я бы не стал делать окончательный экземпляр или локальную переменную (включая параметры метода) окончательно, если только код не требовал ее (например, переменная ссылается от внутреннего класса), или для выяснения некоторой действительно сложной логики.
Например, переменные, я бы сделал их окончательными, если они являются логически константами.
Ответ 11
Существует много применений для переменной final
. Вот лишь несколько
Конечные константы
public static class CircleToolsBetter {
public final static double PI = 3.141;
public double getCircleArea(final double radius) {
return (Math.pow(radius, 2) * PI);
}
}
Это можно использовать тогда для других частей ваших кодов или получить доступ к другим классам таким образом, если вы когда-либо измените значение, которое вам не нужно будет менять один за другим.
Конечные переменные
public static String someMethod(final String environmentKey) {
final String key = "env." + environmentKey;
System.out.println("Key is: " + key);
return (System.getProperty(key));
}
}
В этом классе вы создаете конечную переменную с областью действия, которая добавляет префикс к параметру environmentKey. В этом случае конечная переменная является окончательной только в пределах области выполнения, которая отличается при каждом выполнении метода. Каждый раз, когда вводится метод, окончательный реконструируется. Как только он будет создан, он не может быть изменен во время выполнения метода. Это позволяет фиксировать переменную в методе на время действия метода. см. ниже:
public class FinalVariables {
public final static void main(final String[] args) {
System.out.println("Note how the key variable is changed.");
someMethod("JAVA_HOME");
someMethod("ANT_HOME");
}
}
Конечные константы
public double equation2Better(final double inputValue) {
final double K = 1.414;
final double X = 45.0;
double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M);
double powInputValue = 0;
if (result > 360) {
powInputValue = X * Math.sin(result);
} else {
inputValue = K * Math.sin(result); // <= Compiler error
}
Это особенно полезно, когда у вас действительно длинные строки кодов, и он будет генерировать ошибку компилятора, чтобы вы не запустили логическую/бизнес-ошибку, когда кто-то случайно меняет переменные, которые не следует изменять.
Окончательные коллекции
В другом случае, когда мы говорим о коллекциях, вам нужно установить их как не подлежащие регистрации.
public final static Set VALID_COLORS;
static {
Set temp = new HashSet( );
temp.add(Color.red);
temp.add(Color.orange);
temp.add(Color.yellow);
temp.add(Color.green);
temp.add(Color.blue);
temp.add(Color.decode("#4B0082")); // indigo
temp.add(Color.decode("#8A2BE2")); // violet
VALID_COLORS = Collections.unmodifiableSet(temp);
}
в противном случае, если вы не устанавливаете его как не подлежащее изменению:
Set colors = Rainbow.VALID_COLORS;
colors.add(Color.black); // <= logic error but allowed by compiler
Окончательные классы и Окончательные методы не могут быть расширены или перезаписаны соответственно.
РЕДАКТИРОВАТЬ: ДЛЯ АДРЕСА ЗАДАЧИ ОКОНЧАТЕЛЬНОГО КЛАССА В ОТНОШЕНИИ ЭНЕРГИИ:
Есть два способа сделать окончательный класс. Во-первых, использовать ключевое слово final в объявлении класса:
public final class SomeClass {
// . . . Class contents
}
Второй способ сделать окончательный класс - объявить все его конструкторы как частные:
public class SomeClass {
public final static SOME_INSTANCE = new SomeClass(5);
private SomeClass(final int value) {
}
Отметив это окончательное, вы избавитесь от проблемы, если узнаете, что это действительно окончательный, чтобы продемонстрировать этот класс теста. выглядит с первого взгляда.
public class Test{
private Test(Class beanClass, Class stopClass, int flags)
throws Exception{
// . . . snip . . .
}
}
К сожалению, поскольку единственный конструктор класса является закрытым, невозможно расширить этот класс. В случае класса Test нет причин, чтобы класс был окончательным. Класс Test является хорошим примером того, как неявные конечные классы могут вызывать проблемы.
Итак, вы должны отметить это окончательным, если вы неявно сделаете окончательный класс, сделав его конструктором закрытым.
Ответ 12
Скорее всего, компромисс, как вы упоминаете, но я предпочитаю явно использовать что-то по неявному использованию. Это поможет устранить некоторую двусмысленность для будущих разработчиков кода, даже если это только вы.
Ответ 13
Если у вас есть внутренние (анонимные) классы и метод должен получить доступ к переменной содержащего метода, вам нужно, чтобы эта переменная была окончательной.
Кроме того, вы сказали, что это правильно.
Ответ 14
Используйте ключевое слово final
для переменной, если вы делаете эту переменную как immutable
Объявив переменную как final, она помогает разработчикам исключать возможные проблемы с модификацией переменных в многопоточной среде.
С выпуском java 8 у нас есть еще одна концепция под названием " effectively final variable
". Не конечная переменная может входить в качестве конечной переменной.
локальные переменные, на которые ссылается выражение лямбда, должны быть окончательными или эффективными окончательными
Переменная считается эффективной окончательной, если она не изменяется после инициализации в локальном блоке. Это означает, что теперь вы можете использовать локальную переменную без конечного ключевого слова внутри анонимного класса или выражения лямбда, если они должны быть окончательно окончательными.
До версии Java 7 вы не можете использовать неконфиденциальную локальную переменную внутри анонимного класса, но с Java 8 вы можете
Посмотрите на статью
Ответ 15
Очень простой ответ: мы имеем 3 случая с Финалом с переменными, Final с методами && Финал с классами.
1.Final с переменной, если u u не может присваивать эту переменную более одного раза.
2.Final с помощью методов u не может переопределить этот метод.
3.Final с классами u не может расширять любой конечный класс
Ответ 16
Прежде всего, ключевое слово final используется для создания переменной constant. Константа означает, что он не меняется. Например:
final int CM_PER_INCH = 2.54;
Вы объявите переменную final, потому что сантиметр на дюйм не изменяется.
Если вы попытаетесь переопределить конечное значение, переменная - это то, что было объявлено первым. Например:
final String helloworld = "Hello World";
helloworld = "A String"; //helloworld still equals "Hello World"
Ошибка компиляции, которая выглядит примерно так:
local variable is accessed from inner class, must be declared final
Если ваша переменная не может быть объявлена окончательной или если вы не хотите объявлять ее окончательно, попробуйте следующее:
final String[] helloworld = new String[1];
helloworld[0] = "Hello World!";
System.out.println(helloworld[0]);
helloworld[0] = "A String";
System.out.println(helloworld[0]);
Это напечатает:
Hello World!
A String