I have a class with the following field and method:
private final Map<Character, String> charMap = new LinkedHashMap<>(); public Character charOf(String s) { assert this.charMap.containsValue(s); for (Character c : this.charMap.keySet()) { if (this.charMap.get(c).equals(s)) return c; } }
The compiler doesn’t like this, giving me a “missing return statement” error, whereas this compiles fine:
private final Map<Character, String> charMap = new LinkedHashMap<>(); public Character charOf(String s) { for (Character c : this.charMap.keySet()) { if (this.charMap.get(c).equals(s)) return c; } throw new IllegalArgumentException("There is no mapping for "" + s + """); }
As far as I can tell, these two methods should function exactly the same and do the exact same thing with the former being slighly more readable (at the expense of a less detailed error message). It will always either return a value or throw an exception. Why does the compiler not realize this?
Advertisement
Answer
There are two reasons:
- The complier is not ‘smart’ enough to figure out that
this.charMap.containsValue(s)
being true meansthis.charMap.get(c).equals(s)
must be true for somec
. It only does simpler analysis, like checking if both branches of an if-statement have a return in them. - Even if it were smart enough, Java is a language with mutable objects and threads – even if the map contains the key at the time of the
assert
, it may be removed from the map by another thread before the loop starts.
If you want a language with a sufficiently ‘smart’ compiler, you might want to look at dependently-typed languages like Idris.