2011-12-16 3 views
13

Я продолжаю писать специальные исключения в случае, если опция отсутствует.Ярлык для использования Guava с использованием исключений?

Например:

Optional<?> optional = ...; 
if (!optional.isPresent()) { 
    throw new MyException(); 
} 
Object result = optional.get(); 

Я нахожу этот код не очень свободно, особенно использование челки (!). Я предпочел бы, как писать что-то вроде:

Optional<?> optional = ...; 
Object result = optional.orThrow(MyException.class); 

Есть ли такой ярлык в гуавы, что я еще не нашел?

+6

Общий вопрос. Если вещь, которую вы проверяете, является опциональной, не против концепции «необязательный» иметь метод «Необязательный.orThrow»? –

+4

Полностью согласен. Если 'isAbsent' (добавлено в версии 11) является допустимым условием, это не должно приводить к исключению. Пункт «Необязательный» - это отсутствие действительного не исключительного случая. Если отсутствует исключительный случай, ваш метод должен просто вернуть объект, а также исключить и исключить в отсутствующем случае. –

+2

@black panda: тогда почему я могу присвоить значение по умолчанию с помощью метода 'or()'? Это своего рода разрешение поведения по умолчанию, почему бы и другое? @ John B: Один из вариантов использования - довольно общий экстрактор атрибутов XML: этот атрибут может присутствовать или отсутствовать, поэтому метод возвращает «Необязательный». Затем вызывающий может захотеть принудительно использовать присутствие значения (путем исключения исключения) или получить значение, если оно есть, или даже получить значение по умолчанию. Метод экстрактора просто не знает, что вызывающий объект хочет сделать со значением, поэтому он должен вернуть «Необязательный». Почему этот случай использования недействителен? –

ответ

20

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

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

Это говорит о том, что в этом конкретном вопросе есть совершенно хорошие варианты использования: «если отсутствует, выведите исключение «. Возможно, вы реализуете класс, к которому можно получить доступ в обоих направлениях - один метод с необязательным возвращаемым значением и один метод, который предполагает, что значение всегда будет присутствовать, иначе исключая исключение. Интерфейс Deque, например, предоставляет специальные и исключающие исключения версии peek, poll и offer.

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

if (value.isPresent()) { 
    return value.get(); 
} else { 
    throw new MyException(); 
} 

Метод «orThrow» вы предлагаете требует отражения (!!), не делает позволяют настраивать исключение с полезным сообщением и т. д. «Обычный способ» отлично читается и более эффективен.

Иногда Guava не обеспечивает явной поддержки для вещей, потому что для этих случаев использования мы считаем, что лучше всего делать только «обычный путь». Я думаю, что это так.

+3

Ну, метод 'orThrow()' специально не требует отражения: можно подумать о делегировании инициализации «Исключения» на «Поставщик» или «Функция». В любом случае, главное, что «True Guava Way для этого» повторяется примерно 50 раз с примерно 10 различными исключениями и только с новым кодом, созданным с Guava 10! Мы не хотим дублировать этот код снова и снова. Мы создали несколько вспомогательных методов (1 за конструктор «Исключение» + дополнительные функции), и этого достаточно для нас. Я просто хотел знать, существует ли альтернатива, а это не так. Я не открою билет. –

+0

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

2

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

При этом вы можете создать свой собственный дополнительный класс, который сделает все, что вам нужно. Или вы можете создать свой собственный класс OptionalHelper, где у вас есть метод, который делает то, что вы хотите:

public class OptionalHelper { 
    public <T> T valueOrThrow(final Optional<T> optional) throws MyException { 
     if (optional.isPresent()) { 
      return optional.get(); 
     } else { 
      throw new MyException(); 
     } 
    } 
} 

EDIT:

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

public class OptionalHelper { 
    public <T> T valueOrFail(final Optional<T> optional, final String fieldName) throws OptionalNotPresentError { 
     if (optional.isPresent()) { 
      return optional.get(); 
     } else { 
      throw new OptionalNotPresentError(fieldName); 
     } 
    } 
} 
+0

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

+0

Как бы вы объявили это исключение .newInstance'd в предложение throws? Вы должны иметь общий подкласс Exception, который вы знаете о том, что содержит исключение, которое вы хотите бросить, не так ли? Это кажется мне грязным. –

+0

Это беспорядочный подход. Вот почему они не имеют его в библиотеке, и поэтому я не включил его в свой пример (только добавил его в качестве комментария). Отвечая на ваш вопрос, вы можете уйти с объявлением, если вы расширяете свой класс исключений из RuntimeException (а не из Exception). –

11

Вот еще один способ сделать это без дополнений гуавы:

Object result = optional.or(new Supplier() { 
    public Object get() { 
     throw new MyException(); 
    } 
}); 

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

Object result = optional.or(SomethingIsMissing.INSTANCE); 
1

Это работает для меня (без отражения, просто введите умозаключение):

public class ExceptionSupplier<T, E extends RuntimeException> implements Supplier<T> { 

    private final E exception; 

    private ExceptionSupplier(E exception) { 
     this.exception = exception; 
    } 

    public static <T, E extends RuntimeException> ExceptionSupplier<T, E> throwA(E exception) { 
     return new ExceptionSupplier<T, E>(exception); 
    }  

    public static <T, E extends RuntimeException> ExceptionSupplier<T, E> throwA(@SuppressWarnings("UnusedParameters") Class<T> class_, E exception) { 
     return new ExceptionSupplier<T, E>(exception); 
    } 

    @Override 
    public T get() { 
     throw exception; 
    } 
} 

Использование:

Something something = optionalSomething.or(throwA(Something.class, new SomeException())); 

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

11

Не имеет значения, что Java 8's Optional имеет метод orElseThrow, который позволяет запрашивать поведение.

0

См official issue here

Решение: НЕТ - слишком дорого, а не общий шаблон, можно просто использовать isPresent(), бросить

Поскольку optional.orThrow(new Exception()) не хорошо для работы, я предпочитаю статический импорт, который аналогичен @timk 's answer.

Result result = optional.or(throwException()); 

Равно

Result result = optional.or(SomeSupplier.throwException()); 

статический метод

public static Supplier<Result> throwException() { 
    return new Supplier<Result>() { 
     @Override 
     public Result get() { 
      throw new RuntimeException(); 
     } 

    }; 
} 

===========

Трассировка стека выглядит

Exception ... RuntimeException 
at SomeSupplier$1.get(SomeSupplier.java:line where throw RuntimeException) 
at SomeSupplier$1.get(SomeSupplier.java:1) 
at com.google.common.base.Absent.or(Absent.java:60) 
at line where call optional.or(throwException()); 
+0

Что такое трассировка стека? Как это происходит при отслеживании проблемы, когда вы видите это в журнале? – Ray

+1

@Ray Я думаю, что это шаблон, который генерирует конкретное исключение, а затем где-то его обрабатывает. Более того, журнал можно понять. – Anderson

+0

Спасибо за добавление. Не знал, как анонимный класс участвует в трассировке стека, и я думаю, что это актуально для обсуждения. – Ray

Смежные вопросы