Когда следует создать исключение IllegalArgumentException?

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

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

но кажется, что это заставит следующий дизайн:

public void computeScore() throws MyPackageException {
      try {
          setPercentage(userInputPercent);
      }
      catch(IllegalArgumentException exc){
           throw new MyPackageException(exc);
      }
 }

чтобы вернуть его к проверенному исключению.

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

public void scanEmail(String emailStr, InputStream mime) {
    try {
        EmailAddress parsedAddress = EmailUtil.parse(emailStr);
    }
    catch(ParseException exc){
        throw new IllegalArgumentException("bad email", exc);
    }
}

и хуже - при проверке 0 <= pct && pct <= 100 можно ожидать, что клиентский код будет делать статически, это не так для более продвинутых данных, таких как адрес электронной почты, или, что еще хуже, что-то, что должно быть проверено в базе данных, поэтому в общем клиентский код не может предварительно проверить.

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

6 ответов


API doc для IllegalArgumentException является:

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

глядя на как он используется в библиотеках jdk, Я бы сказал:

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

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

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


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

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

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


выбрасывание исключений среды выполнения "экономно" на самом деле не является хорошей политикой-эффективная Java рекомендует использовать проверенные исключения, когда можно разумно ожидать, что вызывающий абонент восстановится. (Ошибка программиста-это конкретный пример: если конкретный случай указывает на ошибку программиста, то вы должны выбросить непроверенное исключение; вы хотите, чтобы у программиста был след стека, где произошла логическая проблема, а не пытаться справиться с ней самостоятельно.)

Если нет надежда на выздоровление, а затем не стесняйтесь использовать непроверенные исключения; нет смысла их ловить, так что все в порядке.

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


любой API должен проверить правильность каждого параметра любого открытого метода перед его выполнением:

void setPercentage(int pct, AnObject object) {
    if( pct < 0 || pct > 100) {
        throw new IllegalArgumentException("pct has an invalid value");
    }
    if (object == null) {
        throw new IllegalArgumentException("object is null");
    }
}

они представляют 99,9% ошибок времени в приложении, потому что он запрашивает невозможные операции, поэтому в конце концов они являются ошибками, которые должны разбить приложение (так что это не восстанавливаемая ошибка).

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


как указано в официальном руководстве oracle , в нем говорится, что:

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

если у меня есть приложение, взаимодействующее с базой данных с помощью JDBC и у меня есть метод, который принимает аргумент как int item и double price. The price для соответствующего элемента считывается из таблицы базы данных. Я просто умножаю общее число item приобрести с price value и возвращает результат. Хотя я всегда уверен в своем конце (конец приложения), что значение поля цены в таблице никогда не может быть отрицательным .Но что делать, если цена значение выходит отрицательный? Это показывает, что существует серьезная проблема со стороной базы данных. Возможно неправильный ввод цены оператором. Это своего рода вопрос, который другая часть приложение, вызывающее этот метод, не может предвидеть и не может восстановиться из него. Это BUG в вашей базе данных. Итак, и IllegalArguementException() должен быть брошен в этом случае, который будет утверждать, что the price can't be negative.
Надеюсь, я ясно изложил свою точку зрения..


лечения IllegalArgumentException как предпосылки проверьте и рассмотрите принцип проектирования:публичный метод должен знать и публично документировать свои собственные предварительные условия.

Я бы согласился, что этот пример правильный:

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

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

import com.someoneelse.EmailUtil;

public void scanEmail(String emailStr, InputStream mime) throws ParseException {
    EmailAddress parsedAddress = EmailUtil.parseAddress(emailStr);
}

если EmailUtil прозрачен, например, возможно, это частный метод, принадлежащий рассматриваемому классу,IllegalArgumentException является правильным, если и только если его предварительные условия могут быть описаны в документации функции. Это также правильная версия:

/** @param String email An email with an address in the form abc@xyz.com
 * with no nested comments, periods or other nonsense.
 */
public String scanEmail(String email)
  if (!addressIsProperlyFormatted(email)) {
      throw new IllegalArgumentException("invalid address");
  }
  return parseEmail(emailAddr);
}
private String parseEmail(String emailS) {
  // Assumes email is valid
  boolean parsesJustFine = true;
  // Parse logic
  if (!parsesJustFine) {
    // As a private method it is an internal error if address is improperly
    // formatted. This is an internal error to the class implementation.
    throw new AssertError("Internal error");
  }
}

этот дизайн может пойти в любом случае.

  • если предварительные условия дороги для описания, или если класс предназначен для использования клиенты, которые не знают, действительны ли их электронные письма, используют ParseException. Метод верхнего уровня здесь называется scanEmail который намекает, что конечный пользователь намерен отправить неучтенную электронную почту, так что это, вероятно, правильно.
  • если предварительные условия можно описать в документации функции, и класс не намеревается для недопустимого ввода и поэтому указана ошибка программиста, используйте IllegalArgumentException. Хотя не "проверено"," проверка " перемещается в Javadoc, документирующий функцию, которой является клиент должны придерживаться. IllegalArgumentException где клиент не может сказать, что их аргумент является незаконным заранее, неправильно.

примечание о IllegalStateException: это означает ,что " внутреннее состояние этого объекта (переменные частного экземпляра) не может выполнить это действие."Конечный пользователь не может видеть частное состояние настолько свободно, что оно имеет приоритет над IllegalArgumentException в случае, когда клиентский вызов не имеет способа узнать, что состояние объекта несовместимо. Я не имеют хорошего объяснения, когда это предпочтительнее проверенных исключений, хотя такие вещи, как инициализация дважды или потеря соединения с базой данных, которое не восстановлено, являются примерами.