LINUX.ORG.RU

Java: аннотация, ограничивающая вызов метода

 


1

1

В базовом классе есть метод void final setup(...), который должен вызываться только из конструктора. Есть ли аннотации для проверки во время компиляции, что этот метод не вызывается нигде кроме конструктора?

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


Решение

Как подсказал @deadNightTiger, в Error Prone от Google есть аннотация RestrictedApi, которая мне подходит.

Однако, как заметил @Bass, это костыльное решение из-за кривой архитектуры и правильней её исправить, а не подпирать.


Всем спасибо.



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

Если ты такой извращенец, то есть 2 варианта.

1. Из метода сетап раскрутить стек вызовов и кинуть ошибку.

2. Вынеси метод сетап в нестед протектед класс и требуй инстанс этого класса в конструкторе базового класса.

ya-betmen ★★★★★
()
Ответ на: комментарий от RussianWarShip

sealed ввели в 15 чтобы указывать кому именно можно наследоваться, но я им сам пока не пользовался

Lordwind ★★★★★
()
Ответ на: комментарий от ya-betmen

Хотелось бы проверку времени компиляции, а не выполнения. То есть в обработчике аннотации глянуть откуда метод вызывается и в случае чего кинуть ошибку. Метод protected, из сторонних классов недоступен.

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

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

Это XY-problem.

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

Я не буду говорить, что за такое бил бы по рукам. Просто пролистай какую-нибудь древнюю EJ или GoF.

Bass ★★★★★
()
Ответ на: комментарий от ya-betmen

Первый способ не гарантирует переносимости, т. к. на разных JVM (HotSpot, J9,..) стек вызовов, в общем случае, будет разный.

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

Я понимаю, что решение это неказистое, но лучше ничего в голову не приходит.

XY проблема выглядит так. Для описания траектории на двухмерной плоскости есть несколько классов, в том числе AbstractElement как базовый класс для элементов траектории, таких как линия или дуга. Этот базовый класс включает данные о начальной и конечной точках и границы элемента. И если для линии границы вычисляются очень просто, то для дуги это довольно трудоемко и прямо в вызове super() не реализовать.

Можно сделать ленивую инициализацию границ в методе getBound(), но мне такое не нравится. Другой вариант – вызов метода setup() из конструктора линии или дуги.

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

Пишу с мобилки так что может быть криво.

final class Calculator {
    Object initData;
    Calculator (Object initData) {
        this.initData = initData;
    } 
    Object calc() {
        //your logic here
    } 
} 

class SuperPuperBasicClass {
    SuperPuperBasicClass(Calculator calc) {
    Object data = calc.calc();
    //do that you need
} 

class MyClass extends SuperPuperBasicClass {
    MyClass() {
        super(new Calculator(new Object())) ;
} 

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

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

Теперь понял. Но глядя на все эти сложности, я задумываюсь об упрощении базового класса.

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

Ну а разве потомки не должны вызвать родительский конструктор?

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

но это нарушит инкапсуляцию, сломает инвариант базового класса и атомарность инициализации.

хмм, ты же видел, что в джаве появились sealed classes? :) Это ужасно, создатели языка не правы?

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

создатели языкаэффектиные манагеры из оракла не правы?

Поправил

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

У тебя кривой код, думай, как переделать, а не как навесить ещё больше костылей. Скорей всего тебе надо выкинуть все эти наследования и использовать factory method для создания экземпляров.

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

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

Если ты совсем упоротый и чувствуешь в себе святые силы, то можешь написать для Maven плагин с помощью javaparser или чего-то такого, который будет отлавливать все вызовы «запретного» метода и фейлить компиляцию в конце

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

Я не вижу здесь никакого противоречия.

Sealed-классы позволяют создавать ограниченные иерархии наследования. Всё.

Все остальные принципы и требования ООП (включая здравый смысл) остаются в силе.

Искусство писать хороший код определяется, в первую очередь, умением выражать идеи (которым, если говорить про ООП, уже полвека), а вовсе не наличием в языке X синтаксического сахара Y. Typesafe Enum как подход описан у Блоха в EJ задолго до того, как Java 1.5 выразила эту идею на уровне «сахара».

И да, sealed-классы — они только в Java относительно недавно. В Scala и Kotlin они уже сто лет как.

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

Основной смысл sealed классов чтобы исчерпывающий паттерн-матчинг сделать. Это один из кирпичей для алгебраических типов данных.

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

Буду переделывать базовый класс, скорее всего часть методов сделаю абстрактными. Или что-то вроде такого

public final Bound getBound() {
    if (bound == null) {
        bound = calculateBound();
    }

    return bound;
}

protected abstract Bound calculateBound();

@Bass, @vbr спасибо за критику и предложения.

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