Делаем аргументы java-метода окончательными

Какая разница, которую final делает между приведенным ниже кодом. Есть ли преимущество в объявлении аргументов как final.

public String changeTimezone( Timestamp stamp, Timezone fTz, Timezone toTz){  
    return ....
}

public String changeTimezone(final Timestamp stamp, final Timezone fTz, 
        final Timezone toTz){
    return ....
}

person John    schedule 12.11.2010    source источник
comment
Существуют анализаторы кода, которые предупреждают, если параметр используется повторно или переназначен. (То же самое для локальных переменных) IMHO, это лучший способ поймать такие параметры, если вы обнаружите, что их изменение нежелательно.   -  person Peter Lawrey    schedule 12.11.2010
comment
Возможный дубликат Почему я должен использовать ключевое слово final в параметре метода в Java?   -  person james.garriss    schedule 10.10.2017
comment
Я считаю, что Java по умолчанию должна сделать все аргументы метода ввода окончательными. А затем, если я захочу изменить ссылку, мне придется сделать это вручную. Таким образом, фактор вины предотвратит множество таких случаев.   -  person Sid    schedule 16.09.2018


Ответы (11)


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

Это избавляет вас от объявления другой локальной конечной переменной в теле метода:

 void m(final int param) {
        new Thread(new Runnable() {
            public void run() {
                System.err.println(param);
            }
        }).start();
    }
person PeterMmm    schedule 12.11.2010
comment
+1: это важный вариант использования, и единственный раз, когда он вам нужен. (В остальное время это просто вопрос того, чем удобно помогать программисту.) - person Donal Fellows; 12.11.2010
comment
Могу я узнать причину этого? - person KodeWarrior; 18.01.2014
comment
С Java 8 больше не нужно. - person Amit Parashar; 05.11.2015
comment
Итак, с java 8 вы можете получить доступ из внутреннего класса, не будучи окончательным? - person simgineer; 16.02.2018
comment
@simgineer читайте здесь: stackoverflow.com/questions/28408109/ - person PeterMmm; 17.02.2018
comment
@AmitParashar Верно, но Java 8 просто избавляет вас от необходимости использовать ключевое слово final каждый раз, когда вам нужно использовать переменную во внутреннем классе ... Реальность такова, что компилятор просто делает finalality неявно, вам все равно нужно, чтобы переменная была эффективно final ... Таким образом, вы все равно получите ошибку времени компиляции, если попытаетесь назначить ее позже! Java 8: SNEAK 100 :) - person varun; 09.04.2019
comment
@varun согласился +1 - person Amit Parashar; 10.04.2019

Выдержка из Последнее слово на последнем ключевом слове

Окончательные параметры

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

public void doSomething(final int i, final int j)
{
  // cannot change the value of i or j here...
  // any change would be visible only inside the method...
}

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

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

person pgras    schedule 12.11.2010
comment
Возможно, для этого примера лучше использовать объекты, а не примитивы, поскольку примитивные изменения всегда будут видны только внутри метода. А в случае с объектами вы все равно можете их изменить. Вы просто не можете указать на новый объект. Фактически, теперь я думаю об этом, final на самом деле ничего не меняет по сравнению с его отсутствием, кроме сохранения объявления переменной с AIC и указания компилятором случайных изменений параметров, которые вы не хотели изменять по какой-то причине . - person Rob Grant; 15.08.2014

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

Должен признаться, я редко вспоминаю об использовании final для параметров, может, стоит.

public int example(final int basicRate){
    int discountRate;

    discountRate = basicRate - 10;
    // ... lots of code here 
    if ( isGoldCustomer ) {
        basicRate--;  // typo, we intended to say discountRate--, final catches this
    }
    // ... more code here

    return discountRate;
}
person djna    schedule 12.11.2010
comment
Отличный пример того, как может быть полезно объявление аргументов как final. Я неравнодушен к этому, но они также полны рта для 3+ параметров. - person JoseHdez_2; 21.12.2018

Это не имеет большого значения. Это просто означает, что вы не можете писать:

stamp = null;
fTz = new ...;

но вы все равно можете написать:

stamp.setXXX(...);
fTz.setXXX(...);

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

person Adrian Pronk    schedule 12.11.2010

Ключевое слово final при использовании для параметров / переменных в Java отмечает ссылку как окончательную. В случае передачи объекта другому методу система создает копию ссылочной переменной и передает ее методу. Помечая новые ссылки как окончательные, вы защищаете их от переназначения. Иногда это считается хорошей практикой кодирования.

person Sid    schedule 12.11.2010
comment
Я должен кое-что добавить: если параметры примитивны, я не вижу различий. Кроме того, если параметрами являются Коллекции (список объектов ...), добавление final не может предотвратить их изменение. - person Sam003; 23.07.2015
comment
Неизменность - всегда желанная черта. В Java его нет из коробки. Делая переменные final, по крайней мере, обеспечивает целостность ссылок. - person Sid; 23.07.2015
comment
Я согласен. Но если мы действительно хотим добиться неизменности объектов, мы могли бы попробовать сделать глубокий клон. - person Sam003; 23.07.2015

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

person dimitrisli    schedule 12.11.2010

Перечислены два преимущества, которые я вижу:

1 Пометка аргумента метода как окончательного предотвращает переназначение аргумента внутри метода

Из твоего примера

    public String changeTimezone(final Timestamp stamp, final Timezone fTz, 
            final Timezone toTz){
    
    // THIS WILL CAUSE COMPILATION ERROR as fTz is marked as final argument

      fTz = Calendar.getInstance().getTimeZone();     
      return ..
    
    }

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

2 Передача аргумента анонимному внутреннему классу

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

person Santhosh Urumese    schedule 31.12.2019

- В прошлом (до Java 8 :-))

Использование Explit ключевого слова final повлияло на доступность переменной метода для внутренних анонимных классов.

- В современном (Java 8+) языке нет необходимости в таком использовании:

В Java были введены «фактически конечные» переменные. Локальные переменные и параметры метода считаются окончательными, если код не предполагает изменения значения переменной. Поэтому, если вы видите такое ключевое слово в Java8 +, вы можете считать, что оно не нужно. Введение «фактически окончательного» заставляет нас печатать меньше кода при использовании лямбда-выражений.

person Witold Kaczurba    schedule 29.07.2019

Это просто конструкция в Java, которая поможет вам определить контракт и придерживаться его. Аналогичное обсуждение здесь: http://c2.com/cgi/wiki?JavaFinalConsideredEvil

BTW - (как говорится в twiki), маркировка args как final обычно избыточна, если вы следуете хорошим принципам программирования и если вы сделали переназначение / переопределение ссылки на входящий аргумент.

В худшем случае, если вы переопределите ссылку args, это не повлияет на фактическое значение, передаваемое функции, поскольку была передана только ссылка.

person madhurtanwani    schedule 12.11.2010

Я говорю о маркировке переменных и полей как окончательные в целом - не только применительно к аргументам метода. (Пометить методы / классы final - совсем другое дело).

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

person RAY    schedule 12.11.2010

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

Предположим, у нас есть метод

method1 () {

Дата dateOfBirth = новая дата (01.01.2009);

метод2 (дата рождения);

method3 (дата рождения); }

public mehod2 (Date dateOfBirth) {
....
....
....
}

public mehod2 (Date dateOfBirth) {
....
....
....
}

В приведенном выше случае, если dateOfBirth присваивается новое значение в method2, это приведет к неправильному выводу из method3. Поскольку значение, передаваемое методу 3, не то, что было перед передачей методу 2. Поэтому, чтобы избежать этого, последнее ключевое слово используется для параметров.

И это также одна из рекомендаций по программированию на Java.

person Kamal    schedule 12.11.2010
comment
Это не совсем так. Даже если аргумент dateOfBirth будет изменен на другое значение в method2 (), это не повлияет на метод method2 (), поскольку Java передает его по значению, а не по ссылке. - person Flo; 30.06.2013