What is your "favorite" API annoyance or missing feature or misengineered part?
Mine is that java.util.Map.get
does not accept a default object if they key is missing in the Map.
Therefore I find myself too much writing this code:
Object value = map.get(key) == null ? default : map.get(key);
UPDATE
After Pascal's comment showed me how all my code is buggy, should have been:
Object value = map.containsKey(key) ? map.get(key) : default;
java.util.Date
is one and all epic fail. The Calendar
isn't much better. I am really looking toward JSR-310. Until then Joda Time is a perfect alternative.
Edit: Oh, one more comes to mind: java.sql.Connection
, Statement
and ResultSet
doesn't implement kind of Closeable
interface such as Java IO (finally) has since 1.5. You have to write at least three almost the same utility methods to close all of them in a short and proper manner.
BalusC beat me to the date API, so I'll list my second one: The fact that clone()
is totally broken. I don't think there is anything about the API you could define as not broken, including naming.
SimpleDateFormat is not thread-safe. I mean, how hard can that be to fix ?
Oh gosh... I read the JavaDocs of the entire JDK for my Ph.D. and found so many annoyances beyond what already annoyed me from day-to-day programming.
To name a few from my own experience though:
- Swing is too intertwined with AWT. IMHO it is a good example of when you have to give up on inheritance.
- The text in the JavaDoc that provides instructions for people using a method in an interface or a base class is often mixed with the text for people overriding or implementing that method. No real way to overcome this since there is one javadoc per method, but they could have wrote this better.
- Naming that doesn't convey everything (e.g.,
Hashtable
vs.HashMap
and the threading issues). - The date and calendar API. 'nuff said.
That being said, Hindsight is 20/20. It's hard to come up with the perfect API the first time around, and even harder to rectify it while maintaining backward compatibility.
+1 for Date/Calendar API
Also the implementation of generics is pretty bad. Type erasure leads to some weird situations that are difficult to deal with. Also wildcards are confusing from time to time even after using them for years. One more gripe is that the generics syntax is overly verbose:
Map<Integer, String> someMap = new HashMap<Integer,String>();
This will be fixed in Java 7 with generic type inference so the above line will end up looking like this:
Map<Integer, String> someMap = new HashMap<>();
java.util.Stack
deserves a special mention for violating Object Oriented Programming 1-0-1:
Stack
inherits from Vector
but does not obey the "is-a" relationship typical of inheritance relationships. This (mis)implementation makes it possible to perform "illegal" operations on the stack such as inserting an element at an arbitrary position.
I hate the fact that Java does not allow extracting static class metadata at compile (=coding) time. For example, there is no way to statically to refer to a function's name or a field's name for later reflection-use (for example). You must use a dummy constant (final static String FIELD_NAME = "fieldName") instead which is dumb as it could be more easily solved via native meta data reference (think enum and think enum's getName etc. or similar).
However, http://projectlombok.org/ can spice up java just like that.
But why is this not yet native java fuctionality? It should have been for so long now ...
There's a String.split() method, but no String.join(). So annoying.
String comparison:
if ("somestring".equals(foo)) ...
instead of
if (foo == "somestring") ...
"You can't be serious!" -- John McEnroe
The java.math.BigInteger class is a total joke without operator overloading. Something as simple as (a+b)-c becomes (a.add(b)).subtract(c).
Lack of a MultiSet container.
+ the points raised in other posts (especially Date).
that a method having an argument of type
MyClass<AnyType>
leads to the same signature as
MyClass<AnotherType>
does...
My favourite is java.net.URL
. .equals()
and .hashCode()
require network access, because of the silly spec that two URLs are equal if their domain names resolve to the same IP. Not only does this mean that putting them into collections is slow and doesn't work when the network has been firewalled out on a customer's site, but it means that two URLs that will serve different content with HTTP/1.1 will return equal.
The Cloneable interface does not define the clone method (it's just a marker interface). So even if you know that an object implements Cloneable, you don't know whether it can actually be cloned (it might not have a public clone method).
Not so much an annoyance as just plain wrong: there is a case where the following code can enter the final else {...}
part.
if (a==b) { ... }
else if (a<b) {...}
else if (a>b) {...}
else {...}
The case is:
Integer a = new Integer(1); Integer b = new Integer(1);
In case you wonder why: Some comparisons are unboxed, others are not.
Aside from SimpleDateFormat not being thread safe, the equals and hash code methods of java.lang.URL doing external lookups, which is particularly annoying if the addresses point to two different virtual hosts with the same IP address.
ByteBuffer as a Class with no corresponding Interface in NIO.
There is no way to create a "custom" ByteBuffer implementation. (ByteBuffer can not be extended due to access levels of the constructors.)
Closing streams is a night mare.
close() throwing an exception ruins your code and does not help you at all. If you rethrow the exception of close() you loose the original exception and the original exception is usually the important one.
InputStream in = null;
try {
in = new FileInputStream(filename);
// ... some IO operations
} finally {
// This is the ugly part
try {
if (in != null)
in.close();
} catch (IOException e) {
//Ignore this exception (maybe log it)
//but never rethrow it.
}
}
Poor choices for of names are a favourite pet hate.
Classes which extend a class of the same name.
java.sql.Date extends java.util.Date
com.sun.corba.se.spi.orb.ORB extends com.sun.corba.se.org.omg.CORBA.ORB extends org.omg.CORBA_2_3.ORB extends org.omg.CORBA.ORB
Error which is not an error
com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError extends Exception
Exception which is not an exception
javax.print.FlavorException which is an interface
Confusing mix of case
com.sun.org.apache.bcel.internal.Constants.ArrayType which implement equals and hashcode but NOT hashCode()
Stupidly long class name
com.sun.java.swing.plaf.nimbus. InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState
The last one I liked so much I wrote a poem
InternalFrame InternalFrame
Title Pane,
Internal Frame
Title Pane.
Maximize Button Window,
Not Focused State.
I hate how JDBC PreparedStatement
derives from Statement
, and then proceeds to override half of the latter methods (essentially all that make it a "statement") with throwing stubs, replacing them with its own equivalents.
Ultimately, it exposes the design flaw of Statement
class, instances of which - contrary to its name - do not represent statements at all. Instead, they are kinda result cursors in disguise (due to "one open ResultSet
per Statement
" rule), though what's the purpose of having such an object is completely beyond me.
The Exception hierarchy has always been broken in my eyes:
I've always thought it would make more sense for all java.lang.Exception and subtypes to be checked exceptions.
Having java.lang.RuntimeExeption inherit from java.lang.Exception but being unchecked is just broken.
Integer, Double,... comparison.
if (oneDouble.compareTo(otherDouble) > 0)
or
if (oneDouble.isGreaterThan(otherDouble))
and of course all the analog.
(Input|Output)Stream.close() throws a checked exception which means you have to litter your stream handling code with nested try/catch blocks.
My recently found annoyance with Java.
BufferedImages have the functions getRGB and setRGB which are nice, however they return an int and not Color. So if you would like to get the individual components without direct manipulation you would have to create a color object just for that, and then convert it back to int just to set the RGB.
This one is hilarious. You need to go past to check whether the last column read was null
or not.
This is the case when you get 0
from any of these ResultSet
's getInt()
, getDouble()
etc. methods. Then you have to check it whether it was really 0
or null
, by invoking wasNull()
on the ResultSet.
The XML APIs: SAX, DOM, StAX.
I agree that stuffs have improved with StAX, but it is still waaaay to complicated. We can of course blame XML for being an "only apparently" simple technology. Dealing with namespaces, entity, CDATA, etc. can make a trivial problem become a nightmare.
But I still haven't digested the ugliness or reading XML with SAX and DefaultHandler#character()
being sometimes called more than once per tag. Even if there is a rationale for that, it's still bad API to me.
The Boolean
class which defaults to false
.
When a boolean is created with Boolean( String )
or Boolean.valueOf( String )
any value other that "true"
(ignoring case) will lead to a boolean that is false
.
As a consequence, I've seen many property files or configuration with "0"
, or "No"
working correctly, but when toggled to "1"
or "Yes"
there is no effect.
I wish they had been more strict, and that any value other than "true"
and "false"
would yield a BooleanFormatException
in a way similar to Integer.valueOf( String )
.
For other usage we could use parseBoolean( String )
or a BooleanFormatter
...
Collection
interface has contains
method which returns boolean but no find
method. Set
doesn't have find
either, so it ends up with
Map<Type, Type> myMap = new HashMap<Type, Type>();
myMap.put(obj, obj);
or similar.
A decent class for simple monetary calculations. BigDecimal is a right PITA to use if all you want is to do some invoicing for instance.
There isn't even a method to quickly multiply by an integer or calculate a percentage. Would be great to have a class Monetary capable of these things.
I'm thinking along the line $12.98 * 5 * 12.5%
Monetary m = new Monetary("12.98");
Monetary tax = m.multiply(5).percentage(12.5)
A little pet annoyance is that String#format
is static and not an instance method.
I'd like to write:
"My %s format".format("Nice");
instead of
String.format("My %s format", "Nice");
The proliferation of CHECKED exceptions throughout the API. Most notably, java.sql.SQLException and java.io.IOException. They should be unchecked exceptions.
Adding throw statements up your call stack until you get to the method that can handle them is ugly and tedious. And the nested Try-Catch-Finally's to close jdbc connections are a joke.
The collection api is mutable - thats ugly. But why there are to similar types Enumeration
and Iterator
? They just can have made Iterator extends Enumeration
and introduce Enumerable
instead of Iterable
in Java 5. Now you always have to deal with both, adapt one to the other, i hate it.
One of my biggest Java API gripe is that String
's :
.getBytes("UTF-8")
is forcing you to catch a checked exception (UnsupportedEncodingException) which CANNOT happen. ("CANNOT" used as defined by RFC2119)
It cannot happen because if UTF-8 isn't supported by the VM then it's not a compliant JVM for every single JVM under the sun MUST ("MUST" used as defined by RFC2119) support UTF-8 or it is, well, not a JVM.
I've posted about this 10 years ago or so and people have looked at me as if I just landed from a long trip to Mars... Yet of course ten years later a company gave me justice: Google. There's at least one Google Java collection where they acknowledged this as a serious Java API issue and provided a convenience workaround.
Why oh why Java didn't have from the get-go a:
.getUTF8Bytes()
is beyond me.
In a totally ironic turn of event, the fact that they didn't provide such a method made countless typos in "UTF-8" trigger the UnsupportedEncodingException.
Promise I won't start ranting about checked exceptions (I've got my copy of "Effective Java" next to me and I'll read Joshua Bloch ranting about them for me ;)
The inconsistent iterator pattern.
In Java you have the Iterator
and Enumeration
interfaces for iterating over sequences. Moreover, the JDBC class ResultSet
has its own iteration pattern via the next
method. There should be a single, language-supported way to iterate ordered sequences.
The introduction of the for-each loop in Java 1.5 helps for arrays and Iterators, but enumerations and result sets still have their own styles, so if you want to iterate over a sequence you must know what type of sequence it is.