Заключительные аргументы в интерфейсных методах - какой смысл?

190

В Java совершенно законно определять finalаргументы в интерфейсных методах и не подчиняться таковым в реализующем классе, например:

public interface Foo {
    public void foo(int bar, final int baz);
}

public class FooImpl implements Foo {

    @Override
    public void foo(final int bar, int baz) {
        ...
    }
}

В приведенном выше примере barи bazимеет противоположные finalопределения в классе VS интерфейс.

Таким же образом никакие finalограничения не применяются, когда один метод класса расширяет другой, abstractили нет.

Хотя finalимеет некоторое практическое значение внутри тела метода класса, есть ли смысл указывать finalпараметры метода интерфейса?

Миндаш
источник
finalвсе равно ничего не делает с нативными типами, так как они копируются.
Пол Томблин
11
Просто для обсуждения: я только что попробовал, и если два interfaceопределения различаются только по finalатрибуту аргумента, то результирующие .classфайлы одинаково побайтны (и, конечно, javap -vвыдают одинаковый вывод). finalКстати, то же самое верно для двух классов, которые отличаются только по атрибуту!
Иоахим Зауэр
2
@Paul: он делает то же самое, что и со ссылочными типами: он предотвращает изменение самих аргументов (если они используются в реализации).
Иоахим Зауэр
Это имеет такое же значение, как публика в сигнатуре метода.
Робин
2
@Deepak: я вижу, что вы просите рабочие примеры по разным вопросам, даже если кажется, что это не имеет большого смысла. Я думаю, вы должны попытаться научиться абстрактному мышлению: попробуйте подумать о проблеме, не имея перед собой исполняемого кода. Это поможет вам в долгосрочной перспективе.
Иоахим Зауэр

Ответы:

105

Не похоже, что в этом есть смысл. Согласно спецификации языка Java 4.12.4 :

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

Однако finalмодификатор параметра метода не упоминается в правилах сопоставления сигнатур переопределенных методов, и он не влияет на вызывающую функцию, только в теле реализации. Также, как отметил Робин в комментарии, finalмодификатор параметра метода не влияет на сгенерированный байт-код. (Это не относится к другим видам использования final.)

Тед Хопп
источник
Параметр метода также квалифицируется как переменная? Это, очевидно, на практике, но в контексте спецификации?
Миндас
11
Его нельзя использовать для сопоставления подписей, поскольку он не отображается в реальном файле .class. Это только для компилятора.
Робин
@mindas - JLS говорит, что существует семь видов переменных . Параметры метода являются четвертыми в списке.
Тед Хопп
3
Однако документация почти бесполезна, поскольку finalмодификатор не применяется к классу реализации. Ваша подпись интерфейса может просто лгать.
Стефан Хаберл
Спецификация языка Java 8 теперь говорит, что существует восемь видов переменных (вместо семи - они добавили лямбда-параметры ). Параметры метода остаются четвертыми в списке (по крайней мере, некоторые вещи в жизни кажутся стабильными. :-)).
Тед Хопп
25

Некоторые IDE будут копировать сигнатуру абстрактного / интерфейсного метода при вставке реализующего метода в подкласс.

Я не верю, что это имеет какое-либо значение для компилятора.

РЕДАКТИРОВАТЬ: Хотя я считаю, что это было правдой в прошлом, я не думаю, что текущие IDE делают это больше.

Питер Лори
источник
2
Допустимый момент, хотя я не думаю, что было много IDE, когда эта функция была реализована (или оставлена ​​случайно) :-)
mindas
2
Я думаю, что в категории static transientполей. ;)
Питер Лори
1
И публичные конструкторы на абстрактном классе.
Питер Лори
1
IntelliJ делает это. Моя версия IntelliJ IDEA 2016.1.3 Build # IU-145.1617.
Соня
1
@ Сон, это интересно, потому что Android Studio (3.1.4 Build # AI-173.4907809) не делает :(
Крестный отец
18

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

СМГ
источник
6

Обновление: оригинальный ответ ниже был написан без полного понимания вопроса, и, следовательно, непосредственно не затрагивает вопрос. :)Тем не менее, он должен быть информативным для тех, кто хочет понять общее использование finalключевого слова.

Что касается вопроса, я хотел бы процитировать свой собственный комментарий ниже.

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

Но да, звучит довольно странно, что вы можете объявить это finalв интерфейсе, но сделать это не окончательным в реализации. Это имело бы больше смысла, если бы:

а. finalключевое слово не было разрешено для аргументов интерфейса (абстрактного) метода (но вы можете использовать его в реализации), или
b. объявление аргумента как finalв interface заставит его быть объявленным finalв реализации (но не принудительным для нефинала).


Я могу придумать две причины, по которым у сигнатуры метода могут быть finalпараметры: Бины и Объекты ( На самом деле, они обе являются одной и той же причиной, но немного различаются контекстами. )

Объекты:

public static void main(String[] args) {
    StringBuilder cookingPot = new StringBuilder("Water ");
    addVegetables(cookingPot);
    addChicken(cookingPot);
    System.out.println(cookingPot.toString());
    // ^--- OUTPUT IS: Water Carrot Broccoli Chicken ChickenBroth 
    //      We forgot to add cauliflower. It went into the wrong pot.
}

private static void addVegetables(StringBuilder cookingPot) {
    cookingPot.append("Carrot ");
    cookingPot.append("Broccoli ");
    cookingPot = new StringBuilder(cookingPot.toString());
    //   ^--- Assignment allowed...
    cookingPot.append("Cauliflower ");
}

private static void addChicken(final StringBuilder cookingPot) {
    cookingPot.append("Chicken ");
    //cookingPot = new StringBuilder(cookingPot.toString());
    //     ^---- COMPILATION ERROR! It is final.
    cookingPot.append("ChickenBroth ");
}

finalКлючевое слово гарантировало , что мы не случайно создать новый местный банк для приготовления пищи, показывая ошибку компиляции , когда мы пытались сделать это. Это обеспечило добавление куриного бульона в нашу оригинальную кастрюлю, которую addChickenполучил метод. Сравните это с тем, addVegetablesгде мы потеряли цветную капусту, потому что это добавило это к новому местную кастрюлю вместо оригинальной кастрюли, которую она получила.

Бобы: это та же концепция, что и объекты (как показано выше) . Бобы по сути Objects в Java. Однако bean-компоненты (JavaBeans) используются в различных приложениях в качестве удобного способа хранения и передачи определенной коллекции связанных данных. Подобно тому, как addVegetablesможно испортить процесс приготовления пищи, создав новую кастрюлю StringBuilderи выбросив ее вместе с цветной капустой, он также может сделать то же самое с кастрюлей JavaBean .

ADTC
источник
Хорошо. Это не совсем правильный ответ на этот вопрос (поскольку я не обязан заставлять мою реализацию метода интерфейса принимать окончательные аргументы, даже если интерфейс говорит об этом), но он все же полезен, потому что это хорошее объяснение того, почему Создание параметров метода final является хорошей идеей для начала.
Фил
Я считаю, что вы не обязаны реализовывать окончательность аргумента, чтобы вы могли свободно решать, должен ли он быть окончательным или нет в вашей собственной реализации. Но да, звучит довольно странно, что вы можете объявить это finalв интерфейсе, но сделать это не окончательным в реализации. Это имело бы больше смысла, если бы (а) final ключевое слово не было разрешено для аргументов интерфейса (абстрактного) метода (но вы можете использовать его в реализации), или (б) объявление аргумента как finalв интерфейсе заставило бы его быть объявленным finalв реализации (но не принудительно для нефиналов).
ADTC
"Это не совсем правильный ответ на вопрос" Вы правы, я не знаю, о чем я думал ... должно быть, это были ранние крошечные часы июля или что-то в этом роде. :)
ADTC
2

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

(Вроде как объявление методов / членов в интерфейсе как public.)

Питер
источник