Here are some Java tips for making your code more beautiful, understandable, reliable and maintable. They are in no particular order.
- Suppose your code will be read by somebody else. Even if you are working on a project on your own, it's worth commenting properly your code, using interfaces and trying to make your code understanable. That somebody else might be you: in a couple of months you won't remember all the details of your implementation without good comments.
- Comment properly your code: try writing the comment of a function or class before getting into implementation details. That will also give you a better understanding of the problem. Appart from this, there are some very useful comments:
- This interface is not intended to be implemented by clients. Sometimes interfaces are used to hide implementation details. This kind of comment is used to tell the user about this.
- This interface may be implemented by clients. It's almost the opposite of the previous point, except that the library may already provide some implementations.
- This class is not intended to be instantiated by clients. Some classes are used for internal purposes or as results of method invocations. This tells the user they are expected to obtain instances of the class, not to create them.
- This class is not intended to be subclassed. Even if the class is final, this method tells the user it's not correct to subclass some class, because it may lead to poor performance or incorrect behaviour.
- You can obtain an instance of this class/interface from Foo. In case of an interface or a class that is not intended to be instantiated, this helps the user get instances of them.
- See Foo. This tells the user the referenced class is somehow related to the commented class. Even if the referenced class is already mentioned in the general comment, these kind of references are more visible than some word in a big comment.
- Provide clean interfaces between different modules of a system. Typical modules are UI and logic (or core). As much as possible, these modules should communicate using interfaces in order to hide implementation details.
- Use internal packages. Classes that are not supposed to be used outside of a module should be in a separate package hierarchy. Eclipse usually uses the name "internal" for this purpose. For example, in JDT, there's "org.eclipse.jdt.core", "org.eclipse.jdt.ui", and "org.eclipse.jdt.internal.core" and "org.eclipse.jdt.internal.ui". Note that "internal" is the root of all internal packages; you should not name the internal packages, for example, "org.eclipse.jdt.core.internal" and "org.eclipse.jdt.ui.internal". This makes it easier for users browsing the packages to recognize internal packages.
- Use interfaces. Interfaces may have different purposes:
- Provide different implementations for the same goal. The typical example is a list, which may have different implementations for different performance use cases (LinkedList, ArrayList, etc.).
- Allow criteria modification. For example, a sort function may accept a Comparable interface in order to provide any kind of sort criteria, based on the same algorithm.
- Hide implementation details. This also makes it easier for a user to read the comments, since in the body of the interface there are only methods, fields and comments, no long chunks of code to skip.
- Use the I prefix for interfaces. This maks it easier for a user to recognize an interface.
- Treat interfaces that have one implementation differently. Usually interfaces that are used to hide implementation details have a single implementation in the whole system. In these cases:
- Don't prefix a name with implementation details. For example, there could be an IUser interface which has an implementation based on a database. Don't name it DatabaseUser if the chances of changing the implementation are very low; User is enough, with a comment saying "implementation of IUser using a databasea". Long names are harder to read. The User class should be in an internal package.
- Use casts without fear. You interfaces return and accept IUser instances. Since they are interfaces, you might think you need to put in the IUser interface every method needed to accomplish client tasks as well as internal tasks. This is not true. Provide interface methods for things a client might need, and in method implementations cast to the single implementation and use the internally available methods. IUser should be marked with a comment saying a client should not implement it.
- Implement toString() as much as possible. Ideally, every class should implement the toString() method. When you are debugging in Eclipse, it's much easier to see important details of a class by seeing it's toString() information instead of manually expanding lots of nodes. You should't worry about performance details in this method.
- Prefer packages with lots of classes than lots of packages with two or three classes. Having too many packages makes it difficult for a user to browse the source code.
- Write all comments in english. Although in an ideal world I would recommend doing it so in a neutral language, like Esperanto, there's a high chance someone from another country will need to understand your code.
- Write tests, even if it seems impossible to do so. For example, if you need to debug a web page, how can you test correct use of the Request and Response classes? Abstract them with an interface that only provides the methods needed by your application, and use mocks in the tests. Also, try to make your tests as short and self-explaining as possible. If you need to write utility classes just for tests, do so, you won't be losing time.