Разве это не смешно, как предполагаемые самые ничем не примечательные вещи могут привести к спорным дискуссиям или иногда даже жарким спорам с трудными фронтами? Например, я несколько раз был свидетелем того, как использование ключевого слова final
вызывает довольно страстные аргументы. И для стороннего наблюдателя это могло бы выглядеть так, как будто на карту поставлено окончательное решение о том, быть злым или божественным.
Однако следует сказать, что, честно говоря, большинство возможных вариантов final
использования вряд ли укладываются в простой правильный или неправильный шаблон. Выбор использовать или не использовать скорее зависит от индивидуального акцента часто противоречивых намерений.
В поисках советов по литературе единственной точкой соприкосновения на полпути кажется окончательное определение констант …
1
2
3
|
class Foo { public static final String CONSTANT = "constantValue" ; } |
… и пункт 15 Джошуа Блоха: свести к минимуму изменчивость 1 , где он рекомендует сделать все поля неизменяемого класса final
и гарантировать, что класс не может быть расширен (тогда как последний не обязательно должен быть достигнут final
):
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
public final class Foo { private final int value; public Foo( int value) { this .value = value; } public int getValue() { return value; } [...] } |
Оттуда мнения расходятся. Роберт Симмонс-младший В своей книге Hardcore Java 2 он посвятил целую главу final
ключевому слову, в заключение которой он дал твердый совет «распространить final по всему коду». Эта хорошо написанная глава содержит много идей о преимуществах преобразования логических ошибок в ошибки времени компиляции путем объявления переменных, параметров, методов или классов final
.
С другой стороны, Роберт К. Мартин явно не согласен со следующим утверждением: «есть несколько хороших вариантов использования final
, таких как случайная final
константа, но в противном случае ключевое слово добавляет мало значения и создает много беспорядка» 3 . Продолжая, он объясняет, что тип ошибок, которые могут быть обнаружены final
, обычно покрывается его модульными тестами.
Хотя я склонен согласиться с Мартином, я бы не сказал, что Симмонс вообще не прав. В прошлом я фактически использовал final
ключевое слово часто сам, чтобы избежать ошибок в программировании или неправильного использования. Одна из причин, по которой я передумал, — это, вероятно, мой переход на подход TDD пару лет назад.
Сделав это, я заметил — в дополнение к аргументу Мартина — добиться изоляции теста с помощью имитаторов коллаборатора становится гораздо сложнее, если класс соавтора или некоторые его методы объявлены как final
. Поскольку тестирование вряд ли можно считать неправильным использованием , оно заставило меня задуматься о далеко идущих последствиях таких заявлений. Я осознал, насколько сложно предвидеть, что не будет действительного варианта использования, который бы оправдывал расширение и переопределение.
Напротив, сталкиваясь с final
методами или классами, люди иногда проявляют изобретательность, чтобы как-то обойти ограничения, делая вещи, вероятно, хуже, чем, например, расширение классов. Из-за этого в настоящее время я обычно воздерживаюсь от использования ключевого слова в классах и объявлениях методов и ограничиваюсь примечанием, не предназначенным для подклассов, или тому подобным в документации.
Перед тем, как этот пост подходит к концу, я хотел бы поделиться последней мыслью относительно загроможденной темы, упомянутой выше. Для этого взгляните на следующий код, который использует final
для переменных и параметров области действия метода:
1
2
3
4
5
6
7
8
9
|
public void doit( final String message ) { final int value = calculate(); final Item item = create( value, message ); executorService.submit( new Runnable() { public void run() { handle( item ); } } ); } |
Хотя код совершенно бесполезен и может быть организован по-другому, он отражает некоторый реальный стиль кодирования относительно final
я столкнулся в последнее время. Хотя этот стиль предотвращает переназначение локальных переменных при аварии, он также скрывает тот факт, что одно final
объявление фактически является обязательным. Это потому, что item
переменной используется в анонимной реализации Runnable
. Следующий фрагмент избавляется от ненужных объявлений, чтобы подчеркнуть разницу:
1
2
3
4
5
6
7
8
9
|
public void doit( String message ) { int value = calculate(); final Item item = create( value, message ); executorService.submit( new Runnable() { public void run() { handle( item ); } } ); } |
Взвешивая все за и против, я предпочитаю последний вариант, но я полагаю, в зависимости от вашей личной точки зрения, возможностей вашей IDE для выхода из локальных переназначений с предупреждениями, соглашений о кодировании вашей команды, и, и, и, и, вы, вероятно, есть веские причины выбрать первый или второй стиль или даже отдать предпочтение сочетанию обоих.
Что приводит меня к окончательному выводу, что спор будет бушевать!
- Эффективная Java (второе издание), глава 4 — Классы и интерфейсы, Джошуа Блох, 2008 ↩
- Hardcore Java, Глава 2 — Последняя история, Роберт Симмонс-младший, 2004 ↩
- Чистый код, Глава 16, Рефакторинг SerialDate, Роберт С. Мартин, 2009 ↩
Ссылка: | Стиль Java Code: окончательное решение от нашего партнера JCG Фрэнка Аппеля в блоге Code Affine . |