Exceptions are a wonderful thing. They can let your code recover from unexpected situations in a nice and clean way, if you use them properly. That does not mean that you should always rely on them though.
For example, imagine this:
Most highways have guardrails in the middle to avoid cars going into oncoming traffic. While that’s a nice thing, and they indeed drastically reduce head-on collisions, it doesn’t mean you should drive a car with your eyes closed, relying on the guardrail to prevent any accident.
What is that supposed to mean?
Imagine you’ve written a small /warp plugin. This is your current code:
private final Map<String,Warp> warps = new HashMap<>(); @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { try { Warp warp = warps.get(args[0]); // Might cause ArrayIndexOutOfBoundsException Location location = warp.getLocation(); // Might cause NullPointerException ((Player)sender).teleport(location); // Might cause ClassCastException return true; } catch (NullPointerException exception) { sender.sendMessage("The specified warp does not exist."); return true; } catch (ClassCastException exception) { sender.sendMessage("This command is only available for players."); return true; } catch (ArrayIndexOutOfBoundsException exception) { sender.sendMessage("Please specify a warp."); return true; } }
No doubt, the above code will work totally fine, and also display nice messages to the player without throwing any console errors. But, it’s dirty as fuck, and you should never do stuff like that.
Why not?
Exceptions are, as the name suggests, supposed to be used in exceptional situations. It’s totally normal that a player misspells a warp name, or that they don’t enter one at all, or that someone tries to run the command from console, so those situations are not exceptional.
Using exceptions to handle those situations is similar to jumping off a bridge, hoping that there’s a safety net underneath, so you won’t die, instead of not jumping off the bridge in the first place.
The solution
Instead of relying on safety nets or guardrails, you should better just prevent such situations to occur in the first place. Check whether the player entered any arguments, check whether the sender is actually a player, and check if the entered warp actually exists. The above code should instead look like this:
@Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (!(sender instanceof Player)) { sender.sendMessage("This command is only available for players."); return true; } if (args.length == 0) { sender.sendMessage("Please specify a warp."); return true; } Warp warp = warps.get(args[0]); if (warp == null) { sender.sendMessage("The specified warp does not exist."); return true; } ((Player) sender).teleport(warp.getLocation()); return true; }
Conclusion
It is totally possible to use Exceptions to control your code flow, but it’s a very bad idea – it causes unnecessary complexity to your code, it prevents the compiler from doing certain optimizations, and it’s ugly. Provoking exceptions being thrown commonly, just to catch them again, is like jumping off a bridge that has a safety net underneath, when you could just stop jumping off bridges in the first place. Just don’t do it.
me: sees exceptions for code flow control for the first time
me: gorgeous
alex: it sucks
me: it sucks