What I Miss from Java
On Monday, I had a series of tweets ([1], [2], [3]) about what I like about Java that I miss in Objective-C/Cocoa that were probably better off in a blog post. I blame Jeff LaMarche for baiting me into the tweet rant, so here’s the the same thing, in blog post format with more detail and other points I forgot to mention.
What I Miss
Before settling in on Objective-C, I was a Java guy for about six years or so. Overall, I much, much prefer coding in Objective-C to Java, and have no intentions of going back to Java. But that doesn’t mean there’s some things I miss. Here’s a quick list, with more detail below:
- A flexible I/O class hierarchy
- Everything in java.util.concurrent
- Exceptions for errors
- Packages, a.k.a namespaces
- One file for interface/implementation
- Type safe enum classes
- Annotations
- Awesome IDEs (IntelliJ)
A Flexible I/O Class Hierarchy
Overall, I feel the java.io package is really well designed. I like the difference between InputStream/OutputStream that are byte-oriented and Reader/Writer that are text-oriented. Also, there’s some cool subclasses such as GZIPInputStream, GZIPOutputStream, and LineNumberReader
Java’s InputStream and OutputStream are easier to subclass than NSInputStream and NSOutputStream since there’s fewer methods to deal with. Plus, there’s a lingering bug in NSInputStream that makes it effectively impossible to subclass in some really handy situations. See this mailing list post from 2007 that mentions rdar://problem/322278.
Everything in java.util.concurrent
Ever since I read Java Concurrency in Practice, I’ve been drooling over a lot of stuff Java programmers have available in their arsenal in java.util.concurrent. The Executor is roughly equivalent to a Grand Central Dispatch queue and theExecutorService is roughly equivalent to an NSOperationQueue, but that’s where the similarities end. Granted, I think the Cocoa and GCD APIs are more palatable, especially since the introduction of blocks, but there’s a bunch of classes we don’t have that would have been really useful at some point or another:
- BlockingQueue and it’s implementations are handy for passing objects between threads. The need for this is partially mitigated by GCD, but it’d still be nice to have these to feed objects from one queue to another.
- ConcurrentHashMapis an awesome, thread safe and scalable map/dictionary.
- CopyOnWriteArrayList and CopyOnWriteArraySet are really nice for mostly read-only data.
- The atomic classes in java.util.concurrent.atomic are nicer than dealing with the OSAtomic functions.
- The Future interface has some very handy uses. Mike Ash just wrote a blog post on futures in Objective-C, but I think I prefer explicit futures to implicit futures.
- The SwingWorker class offers nice integration of asynchronous background tasks with the user interface thread. I’ve used a similar pattern in Objective-C, but I think it’d be nice to have this encapsulated in a reusable object.
- The java.util.concurrent.locks package has some nice specialty locks such as ReentrantReadWriteLock.
Exceptions for Errors
Objective-C may have exceptions, but they are only used for programming errors or essentially fatal runtime errors. Error handling is done with NSError. This is different than Java, which uses exceptions for expected error cases, such as error reading from a file, or failure to execute a database transaction. This isn’t just a Java thing. Most modern languages like Python, Ruby, and C# use exceptions as Java does. Not using exceptions as error handling was one of the harder habits for me to break; however, I’ve come to embrace NSError and eschew exceptions in Objective-C, even if I don’t like it.
I’ve debated this a number of times with people, but having dealt with both exceptions and NSError, I still prefer exceptions. The big argument against exceptions is that they are non-local jumps causing confusing behavior and subtle bugs. I guess I’ve never seen this happen. Plus there’s also the fact that Foundation and AppKit are not exception safe, so throwing exceptions through these frameworks can and will lead to memory leaks and probably unpredictable behavior. Thus, the best course of action to handle an exception is to gracefully terminate the app.
To me, NSError doesn’t result in more robust code than exceptions. 90% of the time, people just pass NULL or log the NSError in place instead of passing it up to where it’s better handled. A big reason for this is that NSError is returned as an output parameter. Thus to pass an NSError up to a higher level requires that an NSError be added to every method up the chain. Not only is this ungainly, but it’s sometimes not possible to add an NSError to an existing API. This is also a problem with checked exceptions Java, which is why most Java people endorse unchecked exceptions these days.
Exceptions really shine when you call multiple methods in a row that can fail. In Objective-C, your best bet is to return early or use a goto. This still litters your code with error handling that drowns out the real code. With exceptions, the error handling code is nicely separated from the “happy” code path. Perhaps exceptions aren’t the be-all end-all of error handling, as I’ve recently been intrigued by the error handling of Haskell. However, this is not something we’ll see in Objective-C any time soon.
Packages, a.k.a Namespaces
Classes in Objective-C live in a big, flat, global namespace. The common workaround is to prefix class names with two or three letters. Thus, I’d name my classes DDObject or some such. The problem with this approach is that two or three letters is just not enough of a namespace. It helps reduce collisions, but it does not eliminate them. Plus, Apple no longer uses only the NS prefix. It uses CA, CI, CV, QC, AB, UI, PS, IO, QL and probably more. A safe prefix today may be a collision tomorrow.
Things get even more dire when it comes to categories. Category smashing happens, and it’s hard to debug. The only way to avoid it is to prefix your category methods with some unique prefix, as well.
Packages or namespace could theoretically solve both of these collisions. This is not an unknown issue. Many radars have been filed, such as rdar://problem/7025435, that are all duped to a very low radar: rdar://problem/2821039. The problem is this isn’t an easy thing to bolt onto an existing language, because, of course, we want it done in a backwards compatible manner that doesn’t impact the performance of objc_msgSend(). So I understand why we don’t have it, but that doesn’t mean I don’t want it.
One File for Interface and Implementation
There are some good things about the separate of the interface in the .h file and the implementation in the .m file. However, I really despise having to keep these two up-to-date. Many accumulated hours have been lost to compile warnings and errors due to updating one without the other. The repeated method definitions are very un-DRY.
Others suggested that they like this separation of interface from implementation details, only exposing what’s necessary to users of the class, and conceptually I agree. In practice, I find it a chore. Perhaps the repeated code could be mitigated with some Xcode assistance. For example, add a method in the .m and it offers to add it to the .h for you. I think I could deal with this.
However, some of the private details are still exposed in the headers, namely the instance variables. The modern Objective-C runtime, available in 64-bit on Mac OS X and the native iPhone environments, allow synthesized ivars, meaning you can declare properties, and the ivars will automatically be created. But you can’t really use this until you drop 32-bit Mac OS X and/or the simulator supports the modern runtime.
And even so, I’m not sold on synthesized ivars, as it requires using properties where I previously used ivars. To me, a property is conceptually different than an ivar, even if it’s non-public. Properties can be overridden, can have side effects, are available via key-value coding, and also have more overhead. Most of the time, I don’t want these things, and prefer direct ivar access. I much prefer explicit ivars. Perhaps way to solve the exposed privates issue would be to declare them in the .m in say a class extension, instead of the .h. The runtime supports adding ivars at runtime, so this sounds technically possible.
Type Safe Enum Classes
Enums in Objective-C are inherited from C. Thus we get all the weaknesses of C enums for free. C enums are not much more than syntactic sugar for integer constants. There’s no way to get type safety, to introspect them, print them, loop over them, or do anything remotely fancy. The one benefit over a #define used to be that the debugger could decode the integer value into a readable name. But these days, all enums in Cocoa are anonymous, defined as such for 64-bit compatibility:
enum {
// Pass in one of the "By" options:
NSStringEnumerationByLines = 0,
// ...
};
typedef NSUInteger NSStringEnumerationOptions;
Thus most types that are enums are actually typedef’d to NSUInteger. While this is necessary, it means we no longer get debugger integration with enum constants.
Annotations
Annotations in Java are bits of metadata that can be added to classes and methods. Some of this metadata is used at compile time, but it is also available at runtime. The runtime uses are intriguing, but one of the compile time annotations that I like is the @override annotation. This allows you to detect some subtle, yet easy-to-make mistakes when overriding methods in subclasses or a protocol:
- Accidentally overriding a superclass’ method.
- Misspelling a superclass’ method, thus not actually overriding it.
- Misspelling an optional protocol method, thus not actually implementing it.
I’ve requested similar functionality for Objective-C in rdar://problem/7215146.
Awesome IDEs (IntelliJ)
This one has really nothing to do with the language. Xcode has really improved over the years. And while it is generally a very good text editor with project management, I’m afraid it still doesn’t compare to IntelliJ. I don’t know how other Java IDEs stack up (I was never very impressed with Eclipse), but IntelliJ was the first IDE that sold me on the concept of IDEs (I used Emacs prior to it).
Honestly, this probably deserves a whole post in and of itself, but for starters, Xcode could really use intention actions, see rdar://problem/7215136, and much better unit testing integration. IntelliJ has a bunch of other small niceties that all add up to a more pleasant development experience. Unfortunately, it’s been years since I used IntelliJ, so I don’t remember all of the details. Some I can remember are: real-time syntax checking, automatic import statement management, and customizable code reformatting (yes, I need to file bugs on these, too).
All I do remember as that it was the first and only development environment that really took much of the grunt work out of editing code. It not only got out of my way, it actually made me more productive. I dropped the $500 on it out of my own pocket, and it easily paid for itself (in real consulting dollars) in a matter of weeks.