Когда следует использовать Final для параметров метода и локальных переменных?

StackOverflow https://stackoverflow.com/questions/154314

  •  03-07-2019
  •  | 
  •  

Вопрос

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

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

Стоит ли мне постараться запомнить это?

Это было полезно?

Решение

Зацикливайтесь на:

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

Учитывайте, но используйте разумно:

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

Игнорировать, если не чувствуете анала:

  • Параметры метода и локальные переменные. Я РЕДКО делаю это, главным образом потому, что я ленив и считаю, что это загромождает код.Я полностью признаю, что маркировка параметров и локальных переменных, которые я не собираюсь изменять, является более «правильной».Я бы хотел, чтобы это было по умолчанию.Но это не так, и мне кажется, что код сложнее понять, когда финалы повсюду.Если я в чужом коде, я не буду их вытаскивать, но если пишу новый код, то не буду их вставлять.Единственным исключением является случай, когда вам нужно пометить что-то как окончательное, чтобы иметь к нему доступ из анонимного внутреннего класса.

Другие советы

Стоит ли мне приложить усилия, чтобы не забыть это сделать?

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

Преимущества «final» во время разработки по меньшей мере столь же значительны, как и преимущества во время выполнения.Это сообщает будущим редакторам кода кое-что о ваших намерениях.

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

Пометка переменной «final» (и присвоение ее в конструкторе) полезна при внедрении зависимостей.Это указывает на «сотрудническую» природу переменной.

Пометка метода как «окончательного» полезна в абстрактных классах.Он четко определяет, где находятся точки расширения.

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

Тогда вы сможете исправить некоторые ошибки, заменив повторно используемые переменные новыми переменными, объявленными ближе к месту использования.Затем вы обнаружите, что можете заключить целые части метода в фигурные скобки, и вдруг вы находитесь в одном нажатии клавиши IDE от «Извлечь метод», и ваш монстр стал более понятным.

Если ваш метод нет уже не поддающаяся ремонту развалина, я думаю, было бы полезно сделать что-то окончательным, чтобы отговорить людей превращать его в указанную развалину;но если это короткий метод (см.:не является недостижимым), то вы рискуете добавить много многословия.В частности, сигнатуры функций Java достаточно сложны, чтобы уместиться в 80 символов без добавления еще шести на каждый аргумент!

я использую final все время, чтобы сделать Java более основанной на выражениях.См. условия Java (if,else,switch) не основаны на выражениях, что я всегда ненавидел, особенно если вы привыкли к функциональному программированию (например, ML, Scala или Lisp).

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

Позволь мне привести пример:

    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 чтобы получить ошибку компилятора (о которой я никогда не говорил, что вы не можете), но вы можете легко устранить ошибку компилятора, установив имя после оператора переключателя, который отбрасывает семантику выражения или, что еще хуже, забывая 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:метод извлечения).

Вышеупомянутое в OCaml:

type plugin = CandidateExport | JobPostingImport

let p = CandidateExport

let name = match p with
    | CandidateExport -> "Candidate Stuff"
    | JobPostingImport -> "Blah" ;;

А match ... with ... оценивается как функция, т.е. выражение.Обратите внимание, как это выглядит как наш оператор переключения.

Вот пример в схеме (ракетка или курица):

(define name 
    (match b
      ['CandidateExport "Candidate Stuff"]
      ['JobPostingImport "Blah"]))

Ну, это все зависит от вашего стиля...если вам НРАВИТСЯ видеть финал, когда вы не будете изменять переменную, используйте его.Если вам НЕ НРАВИТСЯ это видеть...тогда оставь это.

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

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

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


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

В параметрах полезно избегать случайного изменения значения параметра и возникновения незначительной ошибки.Я обычно игнорирую эту рекомендацию, но потратив около 4 часов.ужасным методом (с сотнями строк кода и множеством for, вложенными if и всевозможными плохими практиками) я бы порекомендовал вам это сделать.

 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 ) { ...

 }

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

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

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

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

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

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

Например, переменные я бы сделал их окончательными, если они логически константы.

Существует множество применений переменной 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) {
  }

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

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

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

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

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

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

В остальном то, что вы сказали, верно.

Использовать final ключевое слово для переменной, если вы создаете эту переменную как immutable

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

В выпуске Java 8 у нас появилась еще одна концепция под названием «effectively final variable". Нефинальная переменная может выступать в качестве финальной переменной.

локальные переменные, на которые ссылается лямбда-выражение, должны быть окончательными или фактически окончательными.

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

До Java 7 вы не можете использовать нефинальную локальную переменную внутри анонимного класса, но с Java 8 вы можете

Посмотри на это статья

Очень простой ответ: у нас есть 3 случая: Final с переменными, Final с методами и& Final с классами.

1. Финал с переменной:вы не можете присвоить эту переменную более одного раза..

2. Финал с методами:вы не можете переопределить этот метод..

3. Финал занятий:ты не можешь расширить какой-либо финальный класс

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

final int CM_PER_INCH = 2.54;

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

Если вы попытаетесь переопределить окончательное значение, переменная будет такой, какой она была объявлена ​​первой.Например:

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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top