Подтвердить что ты не робот

Исключения как общедоступные классы и общедоступные статические внутренние классы

У меня есть два варианта (которые, как я понимаю, технически являются такими же, как я понимаю), чтобы объявить настраиваемый класс исключений, созданный только из определенного класса com.XXX.Foo:

  • как открытый класс в пакете: com.XXX.CustomException
  • как открытый статический внутренний класс: com.XXX.Foo.CustomException

Какой вариант лучше?

4b9b3361

Ответ 1

Если исключение очень специфично для класса Foo, я не против держать его как public Вложенный класс. И всякий раз, когда кажется, что это время, чтобы извлечь это, просто извлеките это.

В обычной практике я никогда не видел вложенного класса, определенного для Exception. Я также не знаю, существует ли в Java API.

Ответ 2

В моем 10-летнем опыте работы с Java я не помню, как сталкивался с API, где открытый класс исключений определялся как статический внутренний класс. Я не могу дать вам конкретную причину, почему было бы плохой идеей сделать это, но это, безусловно, сделало бы ваш код необычным.

Почему вы чувствуете, что это необходимо сделать, учитывая, что (по-видимому) никто не делает? Надеюсь, вы не просто делаете это "инновационным".

(Кстати, я знаю, что некоторые известные Java-API используют общедоступные статические внутренние классы и интерфейсы для других вещей. Я конкретно говорю о случае классов исключений здесь.)

Ответ 3

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

Причины, по которым это не так:

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

Я не нахожу ни одного из этих аргументов вообще убедительными.

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

Но даже без точки "взяты в крайности" рассмотрим исключение, предназначенное для передачи тому классу Foo, которому был дан неправильный ввод. Если я назову это Foo.InvalidInput, имя будет коротким, а связь с Foo пропущена. Если я поставлю его вне класса Foo и назову его FooInvalidCriteria, то я не смогу его повторно использовать из класса Bar, не изменяя его имени (эквивалентно изменению его местоположения).

Но хуже всего, если я оставлю его вне Foo и сохраните его общее имя как InvalidInput. Затем, когда я позже понял, что Bar может также иметь недопустимый ввод и заставить его запускать это исключение. Все компилируется и работает нормально, только теперь все места, которые ловили InvalidInput и предполагали, что они обрабатывают ошибки из Foo, теперь могут также обрабатывать ошибки от Bar, если Foo использует Bar внутренне в способ, который может вызвать выброс этого исключения. Это может привести к поломке кода.

Реальность заключается в том, что принятие исключения, которое ранее было задумано как конкретное указание ситуации, которая возникает в одном классе и повторного использования в качестве общего класса ошибки, - это изменение интерфейса, а не просто изменение внутренней реализации. Чтобы сделать это правильно, вы должны пересмотреть все сайты, на которых было обнаружено исключение, и убедиться, что они все еще верны, поэтому компилятор сообщает вам обо всех используемых сайтах (поскольку вам нужно изменить имя и/или путь импорта ) - это хорошо. Любое исключение, которое вы могли бы сделать статическим внутренним классом, нецелесообразно для повторного использования в других контекстах независимо от того, действительно ли вы делаете его внутренним классом или нет.

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

Ответ 4

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

Пользователь сразу увидит, что существует исключение, когда он смотрит на пакет и не нуждается в чтении файла класса foo, что лучше для обслуживания и ясности/удобочитаемости/соображений. Очень хорошо определить пользовательские исключения и рассказать об этом пользователю API!

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

Тем не менее, мы говорим здесь об основном обычном вопросе!

Ответ 5

Исключения как внутренние классы - плохая идея в целом, потому что по своей природе они могут быть подняты на разных уровнях, даже в простых архитектурах. Класс исключения, который должен быть поднят, должен ссылаться на его содержащий класс, и если этот класс неизвестен пути к классам, что-то плохое, вероятно, произойдет, как ClassCastException.