-
Notifications
You must be signed in to change notification settings - Fork 10.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add factory methods for common exceptions with formatted messages #1382
Comments
Original comment posted by electrum on 2013-04-23 at 10:40 PM Another benefit of this over using checkArgument(false) is that the compiler knows it always throws, allowing use in more situations than checkArgument(false). |
Original comment posted by wasserman.louis on 2013-04-23 at 10:44 PM Additional handy fact: the Preconditions.format implementation is GWT-compatible; String.format is not. If we do decide to do this, it might also be worth supporting the case where you're wrapping another exception with a formatted error message -- e.g. just earlier today I saw some code
I kinda like this request, but like all features that make a single-line operation a shorter single line, it'd need method names that are both good and short to be convincingly worthwhile, I think? Do you have method name suggestions? (FWIW, I would never use checkArgument(false); I'd basically always prefer to either hardcode string concatenation, or use String.format.) |
Original comment posted by jysjys1486 on 2013-04-23 at 11:23 PM IllegalArgumentException IllegalStateException NullPointerException IndexOutOfBoundsException AssertionError UnsupportedOperationException //--------------------------------------------------------------------------- |
Original comment posted by electrum on 2013-04-24 at 12:36 AM The names could be the same as the exception:
|
Original comment posted by electrum on 2013-04-24 at 12:37 AM Related, having a public version of the Preconditions.format() method would be handy. |
Original comment posted by wasserman.louis on 2013-04-24 at 01:04 AM
How would that be different from String.format, other than GWT compatibility?
If you're not using static imports, this seems to be only a few characters shorter than than the alternatives, depending on how you name the class:
versus
|
Original comment posted by electrum on 2013-04-24 at 02:38 AM The big difference is that it's safe if you mess up the format string, which is likely for unexpected conditions that aren't covered by unit tests. |
Original comment posted by ogregoire on 2013-04-24 at 09:27 AM Well, it adds readability. Because this is the real code that we use: throw new IllegalArgumentException(String.format("Invalid authority '%s' found in URI '%s', authority, uri), e); with the cause being the last parameter (where you wrote that the cause was the first parameter, Louis). It's barely acknowledgeable that we have a cause here as it can easily be mistaken with a parameter of the exception. If it went missing for some reason, a reviewer wouldn't notice much. And it might be only at runtime that the error is seen. On the other hand, if the cause is the first parameter, people notice it. I know it's bad to create factory methods just to switch the order of the parameters, but I believe that in this case, the JDK is wrong, and I've thought that for years, even before Java 5 and the varargs syntax. Also, to be honest, with the great work you have done with Guava over the past years, people use static imports a lot; if one library has really popularized those imports, it's Guava. So most of the time, it's not a few characters less: it's nearly half a line less verbose for the same outcome. I would rather use names like "illegalArgument" than "illegalArgumentException", because with the "throw" before it, the context is already huge and we know it's an exception. So yes, we remove a lot of characters to focus on the essential: that we throw, that it's an IllegalArgumentException and that we provide several parameters to the message. Finally, this discussion[1] has some strong arguments against String.format(). Of course, here the context is the logging, which is much more sensitive than the exceptions, but I think that some arguments are solid in this case too. |
Original comment posted by phwendler on 2013-04-26 at 08:48 AM I like the whole idea, and specifically I think removing the "Exception" from the method name is nice and keeps the line more readable. People who want to see the "Exception" part would not use a static import and have "throw Exceptions.illegalArgument" instead of "throw new IllegalArgumentException". throw illegalArgument(e, "Invalid authority '%s' found in URI '%s', authority, uri); is really more readable than throw new IllegalArgumentException(String.format("Invalid authority '%s' found in URI '%s', authority, uri), e); The amount of saved characters might not be that much in total, but it removes all the irrelevant stuff from the beginning of the line. Another nice use case can be public void add(T obj) { where you don't even need the "throw" if the factory method throws itself and the code becomes almost declarative. |
Original comment posted by ogregoire on 2013-04-26 at 09:02 AM No phwend, the factory method shouldn't throw the exception. Transform your method add to make it return a value and you'll see why. |
Original comment posted by phwendler on 2013-04-26 at 09:07 AM ogregoire, why shouldn't it throw the exception (and still keep the exception as the return type)? I know that in cases where you need to tell the compiler that this will always throw, you need to put the "throw" in front of the method call, but if the factory method throws, you don't need to do this everywhere. Even more important, it is now not a problem if you forget the "throw". The same principle is used by the methods in the Throwables class. |
Original comment posted by ogregoire on 2013-04-26 at 09:31 AM It shouldn't throw the exception because this is a factory method. We might still want to do things with the exception prior to throwing it. The pain is in creating the exception because
Throwing it isn't really a problem. This is not the same case as Throwables. Throwables solves a problem linked to the types of exception, where we propagate and stuff like that. Here it's a request to make the creation of the most common exceptions easier. |
Original comment posted by cgdecker on 2013-04-26 at 06:33 PM So, the main reason for Preconditions taking a format string and args is so that it can avoid building a string when no exception needs to be thrown (the majority of the time). In this case, you're always going to be creating and throwing the exception so it's not that valuable. Certainly not worth adding new APIs for, in my opinion. Labels: |
Original comment posted by jamie.spencer.86 on 2013-04-26 at 07:11 PM Is it also practical to include methods that can create any Throwable so long as the Throwable subclass provides the standard constructors? class [Exceptions|Throwables] { public static <X extends [Exception|Throwable]> X [exception|throwable]( Class<? extends X> clas, } This would seem to be a bit more flexible than Supplier<? extends Exception> or specific static factory methods. |
Original comment posted by jamie.spencer.86 on 2013-04-27 at 06:53 PM This is an example: // clas must provide a (String, Throwable) constructor
} |
Original comment posted by [email protected] on 2013-05-03 at 09:21 PM Issue #1164 has been merged into this issue. |
Another thing that could live here is a factory method for |
If we were to do this, we'd want to consider whether to explode out the overloads to avoid boxing and varargs allocation, as we do in
(which allocates some ...or it's likely to run only in the case in which we're actually throwing the exception, as in:
[edit: Oops, I am conflating 2 possible APIs: one that returns an |
from #5927 (comment):
Both true. Additionally, I do still somewhat like this idea, both for the |
(But there is also the general issue of whether such a small improvement justifies all the code-review comments about whether to use that API, even if some tools add review comments to suggest the API automatically.) |
This came up twice in the past 24 hours. In the first discussion, our team was chatting about a hypothetical That discussion served as a reminder of a point that I belatedly made above: There are at least two possible signatures for our factory methods: var inputValue = optional.orElseThrow(() -> illegalArgument(...)); var inputValue = optional.orElseThrow(illegalArgument(...)); Then today, I was reminded that there's actually a third variant that could be useful. That variant would throw the exception, letting us change from... map.merge(
key,
value,
(first, second) -> {
throw new IllegalArgumentException("multiple entries: " + first + ", " + second);
}); ...to... map.merge(
key,
value,
(first, second) -> throwIllegalArgument("multiple entries: %s, %s", first, second)); One nice part about it is that it eliminates the need for a block. But one bad part is that Java doesn't understand that it throws an exception, so it thinks that code afterward would be reachable. Thus, this variant would be unwise to use outside narrow cases like |
Original issue created by electrum on 2013-04-23 at 10:31 PM
It's common to be in a situation where precondition is known to be violated, but the condition can't be checked for directly by a Precondition method. For example:
When this occurs, we want to throw an exception with a formatted message, so we have two options:
The first option has the advantages of being shorter and providing safety in the case of a malformed format string. But it still looks a bit ugly and seems less than ideal.
It would be nice to have factories that take Precondition-like format strings for at least the following common exceptions:
The text was updated successfully, but these errors were encountered: