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

92

Какая разница 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 ....
}
Джон
источник
4
Существуют анализаторы кода, которые предупреждают, если параметр используется повторно или переназначен. (То же самое для локальных переменных) IMHO, это лучший способ поймать такие параметры, если вы обнаружите, что их изменение нежелательно.
Питер Лоури,
Я считаю, что Java должна сделать все аргументы метода ввода окончательными по умолчанию. А затем, если я захочу изменить ссылку, мне придется сделать это вручную. Таким образом, фактор вины предотвратит множество таких случаев.
Сид

Ответы:

131

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

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

 void m(final int param) {
        new Thread(new Runnable() {
            public void run() {
                System.err.println(param);
            }
        }).start();
    }
ПитерМмм
источник
27
+1: это важный вариант использования, и единственный раз, когда он вам нужен . (В остальное время это просто вопрос того, что удобно для программиста.)
Donal Fellows
3
Могу я узнать причину этого?
KodeWarrior
21
С Java 8 больше нет необходимости
Амит Парашар
3
@simgineer читайте здесь: stackoverflow.com/questions/28408109/…
PeterMmm
3
@AmitParashar Верно, но Java 8 просто избавляет вас от необходимости использовать ключевое слово "final" каждый раз, когда вам нужно использовать переменную во внутреннем классе ... Реальность такова, что компилятор просто делает окончательность неявной, вы все равно нужна переменная , чтобы быть эффективно окончательным ... Итак, вы все равно получите компиляции ошибки времени в случае , если вы попытаетесь присвоить к нему позже! Java 8 : SNEAK 100 :)
varun
38

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

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

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

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

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

пгра
источник
18
Возможно, для этого примера лучше использовать объекты, а не примитивы, поскольку изменения примитивов всегда будут видны только внутри метода. А в случае с объектами вы все равно можете их изменить. Вы просто не можете указать на новый объект. Фактически, теперь я думаю об этом, final на самом деле ничего не меняет по сравнению с его отсутствием, кроме сохранения объявления переменной с AIC и указания компилятором случайных изменений параметров, которые вы не хотели изменять по какой-то причине .
Роб Грант
27

Последний не позволяет вам присвоить переменной новое значение, и это может быть полезно для выявления опечаток. Стилистически вы можете оставить полученные параметры неизменными и назначить их только локальным переменным, поэтому 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;
}
джна
источник
1
Отличный пример того, как может быть полезно объявление аргументов как final. Я неравнодушен к этому, но они также полны рта для 3+ параметров.
JoseHdez_2
14

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

stamp = null;
fTz = new ...;

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

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

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

Адриан Пронк
источник
3

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

Сид
источник
1
Надо кое-что добавить: если параметры примитивны, то различий не вижу. Кроме того, если параметрами являются Коллекции (список объектов ...), добавление final не может предотвратить их изменение.
Sam003
1
Неизменяемость - всегда желанная черта. В Java его нет из коробки. Делая переменные final, по крайней мере, обеспечивает целостность ссылок.
Сид
1
Согласен. Но если мы действительно хотим добиться неизменности объектов, мы могли бы попробовать сделать глубокий клон.
Sam003
2

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

Димитрисли
источник
2

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

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.

Сантош Урумезе
источник
1

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

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

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

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

Витольд Качурба
источник
0

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

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

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

Мадхуртанвани
источник
0

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

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

РЭЙ
источник
-3

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

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

method1 () {

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

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

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

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

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

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

И это тоже одна из лучших практик программирования на Java.

Камаль
источник
5
Это не совсем так. Даже если аргумент dateOfBirth будет изменен на другое значение в method2 (), это не повлияет на method2 (), поскольку Java передает значение по значению, а не по ссылке.
Flo