Все окончательно

Я использую PMD, чтобы помочь обнаружить потенциальные проблемы в моем Java-коде, и я обнаружил, что в нем советуют разделить на полезное, идиосинкразическое, и «WTF ?!».

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

Есть ли конкретные преимущества / недостатки у навешивания final на каждое объявление переменной, которое вы, возможно, можете?


person BlairHippo    schedule 24.08.2010    source источник
comment
См. stackoverflow.com/questions/ 137868 /   -  person Colin Hebert    schedule 24.08.2010


Ответы (5)


«Любое объявление переменной, которое вы можете» звучит немного экстремально, но final на самом деле полезно во многих отношениях. Иногда мне хочется, чтобы final был поведением по умолчанию и не требовал ключевого слова, но истинные «переменные» требовали модификатора variable. Scala приняла что-то вроде этого подхода с ключевыми словами val и var, использование val (похожее на final ключевое слово) настоятельно рекомендуется.

Особенно важно тщательно продумать, является ли каждая переменная-член final, volatile или ни одной, потому что от этого зависит безопасность потоков класса. Значения, присвоенные переменным final и volatile, всегда видны другим потокам без использования блока synchronized.

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

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

Было сделано предложение добавить в Java ключевое слово var для поддержки вывода типов. Но в рамках этого предложения был внесен ряд предложений по дополнительным способам определения неизменяемости локальной переменной. Например, было предложено также добавить ключевое слово val для объявления неизменяемой переменной с предполагаемым типом. С другой стороны, некоторые выступают за совместное использование final и var.

person erickson    schedule 24.08.2010
comment
Оглядываясь назад, это было бы лучше всего. Однако это нарушило бы семантику C, которая явно была призвана дать программистам на C ++ легкий переход. - person Thorbjørn Ravn Andersen; 25.08.2010
comment
Компилятору JIT вообще не требуется final для локальных переменных, и никогда этого не требовал; он может видеть, действительно ли изменена каждая переменная. Конечно, то, что он видит, и то, что видит программист, потенциально совершенно разные ... - person Donal Fellows; 12.02.2019
comment
final не гарантирует потокобезопасность без синхронизации. final List может вызвать исключение ConcurrentModificationException, если вы измените размер списка, в то время как другой поток выполняет его итерацию. То же самое и со сложными объектами, такими как DAO. Даже если сам DAO является окончательным, какой-то поток может испортить свойства, пока другой поток их читает, и создать несогласованные состояния, поэтому вам понадобится потокобезопасный DAO / List, чтобы гарантировать, что окончательные гарантии безопасности потока - person DGoiko; 29.01.2020
comment
@DGoiko Я не говорил, что это так, но спасибо за разъяснения. - person erickson; 29.01.2020
comment
о, извините, английский - не язык моей матери, и я могу показаться грубым. Я просто хотел сделать примечание - person DGoiko; 29.01.2020
comment
@DGoiko Нет проблем, многие читатели оценят эту дополнительную информацию. - person erickson; 29.01.2020

final сообщает читателю, что значение или ссылка, назначенные первым, остаются такими же в любое время позже.

Поскольку все, что МОЖЕТ быть окончательным, ЯВЛЯЕТСЯ окончательным в этом сценарии, отсутствующий финал сообщает читателю, что значение будет измениться позже, и принять это во внимание.

person Thorbjørn Ravn Andersen    schedule 24.08.2010
comment
Это имеет смысл для примитивов или неизменяемых объектов, таких как String. Но вызывает ли это ложное чувство безопасности для изменяемых объектов? - person BlairHippo; 24.08.2010
comment
Отсутствующий финал только говорит вам, что он может измениться позже, но никак не об этом. Предполагать так просто неправильно. Если класс не определен как окончательный, он может быть разделен на подклассы, но это не значит, что это так. Использование final для всех переменных открыто для обсуждения (часто здесь), поэтому его отсутствие можно так же легко отнести к предпочтениям кодировщика. - person Robin; 24.08.2010
comment
@Robin, обратите внимание, что OP сказал, что все окончательно. Это тот случай, который я обсуждаю. Пожалуйста, внимательно прочитайте вопросы, прежде чем голосовать против. - person Thorbjørn Ravn Andersen; 25.08.2010
comment
@BlairHippo, только если вы не понимаете разницу между самой ссылкой и ссылочным объектом. Что вам гарантировано, так это то, что у вас есть то же самое - что внутри вещи - другое дело (но в этом конкретном случае есть большая вероятность, что финалов тоже будет много) - person Thorbjørn Ravn Andersen; 25.08.2010

Это обычная идиома для таких инструментов, как PMD. Например, ниже приведены соответствующие правила в Checkstyle. Это действительно вопрос стиля / предпочтений, и вы можете возразить за обе стороны.

На мой взгляд, использование final для параметров метода и локальных переменных (если применимо) - это хороший стиль. Идиома «дизайн для расширения» спорна.

person Taylor Leese    schedule 24.08.2010

PMD также имеет правила опций, которые вы можете включить, которые жалуются на final; это произвольное правило.

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

person Dean J    schedule 24.08.2010

Вот почему может быть полезно пометить почти все как 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) {
  }

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

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

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

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

person mel3kings    schedule 21.06.2013