LINUX.ORG.RU

Про final в Java замолвите слово

 , , ,


0

1

Последнее время у меня появились сильные загоны на перфекционизм. Глядя на продакшен код проектов понимаешь, что он говно, и хочется хотя-бы в своей уютной репе всё делать по канонам. Большинство вопросов у меня возникло по final в разных контекстах его применения, которые хотелось бы тут обсудить (без срача, пожалуйста, только аргументы и факты).

Первое - классы. Если опираться на трактовку Open Closed принципа, то мы должны иметь возможность отнаследоваться от класса, чтобы расширить его функциональность, потому final классами должны быть как правило только утилитные классы, либо то, что логически по тем или иным причинам не должно использоваться как родитель чего-либо.

Методы - а вот тут уже интереснее, опять же возвращаемся к трактовке Open Closed, правильно ли я понимаю, что все методы класса должны быть помечены как final? Т.е. даже если кто-то отнаследуется от класса, он сможет только докинуть своих методов, но не сможет переопределить то, что уже имеется. Такой же вопрос к методам-реализациям. Если класс, допустим, является реализацией какого-либо интерфейса, должны ли Override методы из этого интерфейса быть final?

Переменные: Локальные - да, если не меняются на протяжении области видимости, в сигнатуре функции судя по всему тоже да, чтобы не было возможности сделать вот так

public void foo(String s) {
    s = "New String";
}

Соб-но, дискасс, а где и как ты, ЛОРовец, используешь final и как видишь данную ситуацию?

★★★★

когда коту делать нечего...

ну ничего, придет время и о логике задумаешься

Deleted
()

Гораздо интереснее вопрос: можно ли десяточку накатывать в продакшен?

Deleted
()

Если класс, допустим, является реализацией какого-либо интерфейса, должны ли Override методы из этого интерфейса быть final?

С чего бы?

Tanger ★★★★★
()
Ответ на: комментарий от Tanger

Ну, например с того, что другой класс может отнасдедоваться от него и переопределить метод-реализацию.

Jefail ★★★★
() автор топика

Соб-но, дискасс

ООП - дерьмо. Не нужен никакой дискасс. /thread

crutch_master ★★★★★
()
Ответ на: комментарий от Jefail

Ну, например с того, что другой класс может отнасдедоваться от него и переопределить метод-реализацию.

Чем это плохо? Зачем это запрещать? В большинстве случаев тебе не final нужно, а private.

Tanger ★★★★★
()

Последнее время у меня появились сильные загоны на перфекционизм. Глядя на продакшен код проектов понимаешь, что он говно

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

Следить за стилем можно посредством статических анализаторов кода вроде checkstyle или findbug, есть плагины в ide, можно еще sonar использовать и бить по рукам в случае залития ворнингов.

Aber ★★★★★
()

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

r0ck3r ★★★★★
()

Так это не только про java, в крестах те же вопросы можно поднимать.

Члены класса - всегда по возможности const/final, локальные - чаще нет, у них контекст виден. Зачем нужен final у классов/методов я не знаю, разве что в редких случаях вызов метода получится быстрее.

anonymous
()
public void foo(String s) {
    s = "New String";
}

Кажется это статическим анализатором можно отловить, используйте например checkstyle, следите чтоб в коде не было ворнингов, когда добавляете новые чеки(правила) выделяйте время (создавайте баг на трекере) на приведение кода в порядок.

Aber ★★★★★
()
Последнее исправление: Aber (всего исправлений: 1)

Прочитай «Effective Java» Джошуа Блоха.

Сразу ряд вопросов отпадёт.

Bass ★★★★★
()
Последнее исправление: Bass (всего исправлений: 1)

Методы - а вот тут уже интереснее, опять же возвращаемся к трактовке Open Closed, правильно ли я понимаю, что все методы класса должны быть помечены как final?

Переопределять метод из неабстрактного класса уже code smell, дискасс.

spec_po_kiskam ★★★
()
Ответ на: комментарий от r0ck3r

Но это, как мне кажется, противоречит трактовке Open for extension, closed for modification. Либо я трактую это не правильно.

Jefail ★★★★
() автор топика
Ответ на: комментарий от r0ck3r

Это академический ООП, который в упражнениях где корова наследуется от жирафа и прочее говно. Правильный дизайн это «composition over inheritance».

spec_po_kiskam ★★★
()
Ответ на: комментарий от spec_po_kiskam

Переопределять метод из неабстрактного класса уже code smell, дискасс.

Это пахнет ООП из 2000-х, когда модно было делать километровые иерархии классов.

oldstable
()
Ответ на: комментарий от oldstable

Километровые иерархии абстрактных классов? Ты о чем? Я о том, что оверрайдить методы нужно чуть менее чем никогда, дискасс.

spec_po_kiskam ★★★
()
Ответ на: комментарий от spec_po_kiskam

Во, удваиваю. Я сторонник идеи, что если какое-то поведение должно быть гибким, то сделать интерфейс и протащить его через конструктор, а там уже пускай фабрика / DI Framework решает, что должно быть.

Jefail ★★★★
() автор топика

вот пример класс структурка

public class Point {

    public final int x;
    public final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

final еще и помогает не забыть поле инициализировать.

barberry ★★
()
Ответ на: комментарий от spec_po_kiskam

Если у кого-то возникла ситуация, когда корова наследуется от жирафа, ему следует менять профессию. А посмотреть массу примеров переопределения методов родительского не абстрактного класса можно в Android SDK и понять когда и где это может быть нужно

r0ck3r ★★★★★
()
Ответ на: комментарий от r0ck3r

Я как Android разработчик с почти 4х летним стажем скажу, что говна в этом самом Android SDK огромное количество. На него не всегда можно и стоит равняться.

Jefail ★★★★
() автор топика
Ответ на: комментарий от Jefail

Равняться, может, и не стоит, но зато он наглядно показывает с чем придется столкнуться разработчику, если проект, над которым он работает, долгосрочный

r0ck3r ★★★★★
()
Ответ на: комментарий от Jefail

Я как Android разработчик с почти 4х летним стажем с

Кстати да, Kotlin!

Aber ★★★★★
()
Ответ на: комментарий от FilosofeM

Пишу проект на котлине целиком, но не очень нравится, слишком хайповый, да и байткод на выходе плохой.

Jefail ★★★★
() автор топика

Вообще, по уму, final надо ставить везде, где только можно. Наследование — это вообще зло, и использовать его надо аккуратно и изредка. Наследование от неабстрактных классов — зло полнейшее, его использовать нельзя никогда. Поэтому ВСЕ неабстрактные классы надо пометить как final. А если кто хочет расширить функциональность твоего класса, пусть использует композицию. Меньше проблем огребет.

Про локальные переменные я не понял если честно.

morse ★★★★★
()

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

trex6 ★★★★★
()
Ответ на: комментарий от morse

Вам бы, уважаемый, с реальным кодом реальных проектов поработать. Академическая среда, это конечно хорошо, но качество кода там так себе.

trex6 ★★★★★
()
Ответ на: комментарий от trex6

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

И качество кода в реальных проектах, надо сказать, тоже атас. Но человек спрашивает как оно там в мире розовых пони и идеальных проектов. А там оно вот так.

morse ★★★★★
()
Ответ на: комментарий от t184256

Полиморфизм? Ну что ж, рад за Вас. Вообще переопределение родительских не абстрактных методов в Java - норма.

Стоит отметить, что все классы автоматически наследуются от Object, который также не является абстрактным. Также и с JFrame, который расширяют и переопределяют конструктор

r0ck3r ★★★★★
()
Последнее исправление: r0ck3r (всего исправлений: 1)
Ответ на: комментарий от r0ck3r

может это и не является best practices и лучше наследоваться только от абстрактных классов, но на практике это не работает, так часто бывает нужно расширить функционал какой-то части приложения без переписывания старого кода и с минимумом затрат. Здесь выходом является расширение класса с переопределением «проблемных» методов. В связи с этим я и привел в пример Android SDK

r0ck3r ★★★★★
()
Последнее исправление: r0ck3r (всего исправлений: 1)
Ответ на: комментарий от r0ck3r

В Android чаще всего переопределение является делегированием, изначально в архитектуру заложенным (тот же onCreate в Activity), это немного другое. Если посмотреть на исходники OkHttp / Retrofit, то хрен там получится от чего-то отнаследоваться и что-то проблемное переопределить, руки разработчика в конечном API весьма сильно связаны.

Jefail ★★★★
() автор топика
Ответ на: комментарий от r0ck3r

И это, кстати, очень дерьмовый подход. Например, вот дефолтная реализация метода onCreateView у фрагмента

    @Nullable
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        return null;
    }

Это просто пустышка, но от того, что будет возвращего переопределеним очень многое меняется в поведении, ссылка на вьюху где-то неявно сохраняется, но, самое главное, родитель явно закладывается на наследника, хотя он вообще о нём ничего знать не должен.

Jefail ★★★★
() автор топика
Ответ на: комментарий от DarkAmateur

Помогает ли final в оптимизации?

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

barberry ★★
()

Возьми pdf-ку «Bruce Eckel. Thinking in Java» и тупо просканируй на слово «final» (хотя там есть отдельный раздел на эту тему). Сомневаюсь, что к этому можно будет еще что-то существенное добавить. Разве что, конкретные примеры из личной практики...

vinvlad ★★
()
Ответ на: комментарий от r0ck3r

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

Суть ООП в том что это ООП. man понты.

А если откинуть все эти понты, то метод, это обычная функция с this в первом аргументе. Тогда this это просто набор данных и никакого полиморфизма нахер не нужно, ведь любые функции и так доступны.

deep-purple ★★★★★
()
Ответ на: комментарий от r0ck3r

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

Да, будет наследовать корову от жирафа ))

deep-purple ★★★★★
()

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

А как же классы которые собой представляют просто данные и ничего больше? Также почитай JSR133 (к слову об оптимизации и потоко безопасности)

Int64 ★★★
()
Ответ на: комментарий от r0ck3r

Полиморфизм исключительно через щель копирования реализации с расширением. Это ни в коем случае не противоречит ООП, это даже, как ты правильно отметил, много где разрешено. Но «сутью ООП» это, слава богам, не является.

t184256 ★★★★★
()
Ответ на: комментарий от r0ck3r

Если у разработчиков проекта низкая квалификация, то конечно. Или если разрабатываешь публичный API, где навертев говна, так просто не отрефакторить.

spec_po_kiskam ★★★
()
Ответ на: комментарий от barberry

Привет из функционального мира. Если начал писать на хаскелле, трудно остановиться.

spec_po_kiskam ★★★
()
Ответ на: комментарий от barberry

Странно, я, в общем, так до сих пор делаю, что на Java, что на C. Правда в C, это действительно микрооптимизация, особенно, если многопоток.

DarkAmateur ★★★
()
Последнее исправление: DarkAmateur (всего исправлений: 1)
Ответ на: комментарий от deep-purple

нахер не нужно, ведь любые функции и так доступны

И как определить, какую функцию вызывать, без таблицы виртуальных методов?

TheAnonymous ★★★★★
()
Ответ на: комментарий от TheAnonymous

По названию функции и её тайпхинтингу когда код пишешь:

void someFnForTypeOne(TypeOne obj, int arg1);
ООП это понтованый сахарок, ну да, с некоторыми оговорками.

deep-purple ★★★★★
()
Ответ на: комментарий от DarkAmateur

Правда в C, это действительно микрооптимизация

В C это вообще не оптимизация, по крайней мере для локальных переменных

anonymous
()

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

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

vtVitus ★★★★★
()
Ответ на: комментарий от morse

Наследование от неабстрактных классов — зло полнейшее, его использовать нельзя никогда

Это ты какой-то совсем идеальный случай описываешь.

В том же Android часто бывает нужно отнаследоваться от какого-нибудь конкретного View, чтобы добавить или переопределить некий метод. В том, что это не очень красиво – да, но что совсем «никогда нельзя» – слишком резко.

mono ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.