Code formatting
The following example illustrates the way a class should be formatted. Only pay attention to the code formatting, the class itself does not have meaningful code.
- Download the IntelliJ template. Drop this template in your $INTELLIJ_USER_DIR/config/codestyles directory (Ex. 'C:/Documents and Settings/rob/.IntelliJIdea50/config/codestyles'. IntelliJ must not be started while doing this!) and select it in the 'Project Code Style' option in the Settings menu.
/*
* Copyright 2008 Stichting JoiningTracks, The Netherlands
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.joiningtracks.his.conventions.code;
import org.joiningtracks.his.service.*;
import java.util.List;
import java.util.Date;
public class DefaultCodeConventions implements CodeConventions {
private static final Logger logger = LogManager.getLogger(DefaultCodeConventions.class);
private int count;
private String lastMessage;
private List<Date> dates;
private MessageService messageService;
public void methodName(String message, boolean condition) throws SensibleException {
Assert.notNull(message, "Message can not be null");
do {
lastMessage = message;
count++;
} while (true);
try {
messageService.send(message);
} catch (MessageException e) {
logger.error("Failed to send message: " + message, e);
throw new SensibleException(e);
} finally {
notifyAdministrator();
}
if (condition) {
dates.add(new Date());
} else if (count > 10) {
throw new SensibleException("Can only send 10 messages");
} else {
notifyAdministrator();
}
for (int i=0; i<5; i++) {
doSomething();
}
switch (count) {
case 0:
doCase0();
break;
default:
doDefault();
}
}
private class MessageConverter extends BaseClass {
public String convertMessage(String message) {
return message + "(converted)";
}
}
}
Some things to look out for:
- Having a blank line between the class declaration and the first variable declaration can help making the class easier to read. However, this is not enforced.
- When using Spring, make use of Assert.notNull(...) to check for null arguments.
- do {} while(...) looks are *highly* discouraged.
- Don't initialize member variables along with their declaration - use a constructor for that.
- Reuse the different constructors in the class to avoid code duplications by calling this(...).
- String concatenation of *dynamic* strings that spans multiple statements should be done using StringBuilder?
Indentation
The line length should be 120 characters.
If a line exceeds 120 characters, it will need to be wrapped. This can be done according to the following rules:
- Break after a comma.
- Break before an operator.
- Prefer higher-level breaks to lower-level breaks (in other words avoid breaking nested expressions).
- Indent 8 spaces on the new line.
- If applied to a method signature, prefer to put each argument below one another.
Examples:
// prefer higher-level breaks
longName1 = longName2 * (longName3 + longName4 - longName5)
+ 4 * longname6;
// for method signatures, put each argument below one another and align them with the first argument to clearly seperate it from the body
someMethod(int anArg,
Object anotherArg,
String yetAnotherArg,
Object andStillAnother) {
...
}
// indent 8 spaces here, because aligning it makes the logic hard to read.
if ((condition1 && condition2) || (condition3 && condition4)
||!(condition5 && condition6)) {
doSomethingAboutIt();
}
Comments
Comments come in two forms: block comments (/* */) and line comments (// ). Block comments are rarely used and highly discouraged, Only use them to disable portions of code that should not get executed. Line comments are used to explain the code that follows. They are always on a seperate line and above the code it describes. They never trail a piece of code and are never places at the end of a line.
Examples:
public void someMethod() {
...
// this is a line comment
// even for multiple lines
messageService.send(message);
/*
notifyAdministrator();
logger.info("Sent message to administrator");
*/
count++; // DO NOT USE this comment style
}
Declarations
Declarations of fields should stick to one on each line. Avoid aligning the fieldnames.
public class Declarations {
// cotrect
int number;
String text;
// DO NOT align fieldnames
Date expiryDate;
List<String> driverNames;
Avoid naming a local variable the same as a higher-level variable:
int count;
...
myMethod() {
if (condition) {
int count = 0;
...
}
...
}
White space
Placing a white space or blank lines largely follows common sense. There are however some points that deserve extra attetion.
A keyword followed by a parenthesis should be separated by a space. Example:
while (true) {
...
}
if (condition) {
...
}
Note that a blank space should not be used between a method name and its opening parenthesis. This helps to distinguish keywords from method calls.
Operands and casts should be seperated with a white space as well.
(a + b) / (2 - 4) someMethod((int) d);
Logging
When logging info, debug or trace statements, always put the logging in an if-statement (logger.isXEabled()) (where X is either Debug‚ Info, or Trace).
Exceptions
Good practice is to declare sensible runtime exceptions. Sensible means, that there is no point in declaring a NullPointerException?, but an IllegalArgumentException? sometimes makes sense as long as it is explicitly being checked in the body of the method - and of course custom runtime exceptions as well.
Whenever a class/interface API is being missued a runtime exception should be thrown. Misusing an API is simply disregarding the class/interface contract. The contract is defined by method signatures (return value, declared exceptions (checked and runtime, and *documentation* of the method and the containing class).
Naming conventions
| Identifier type | Conventions | Examples |
| Packages | Yet to be determined | |
| Classes | Class names should be nouns, in mixed case with the first letter of each internal word capitalized. Try to keep your class names simple and descriptive. Use whole words-avoid acronyms and abbreviations (unless the abbreviation is much more widely used than the long form, such as URL or HTML). If an acronym is used, then only capitalize the first letter (e.g. HtmlParser? instead of HTMLParser) | Account \\ LogProcessor? \\ HtmlParser? |
| Interfaces | The same for classes applies to interfaces. Do not use an extra identifier to emphasize it is an interface, like IAccount or AccountInterface? | AccountService? \\ UserDao? \\ Plugin |
| Methods | Methods should be verbs or express an active tense, in mixed case with the first letter lowercase, with the first letter of each internal word capitalized. Try to avoid very long method names, but do not cut in the expressiveness of the name. | viewAccount() \\ getUserById() \\ calculateRevenue() |
| Variables | All variables are in mixed case with a lowercase first letter. Internal words start with capital letters. Variable names should not start with underscore _ or other meaningless characters. \\ \\ Variable names should be short yet meaningful. The choice of a variable name should indicate to any reader the intent of its use. One-character variable names should be avoided except for temporary "throwaway" variables (e.g. 'i' in for-loops) | firstName \\ count \\ revenueStrategy |
| Constants | The exception to variable names are class constants that don't represent services; they should be all uppercase with words separated by underscores ("_"). | DEFAULT_LOGIN_URL \\ IFN_CODE \\ MINUTES_IN_DAY \\ logger (a service) |
| Database tables | All tablenames should be in the same case, preferrably lowercase | ACCOUNT \\ USER \\ PRODUCT |
Class structure
- Break long and complex method into several helper method
- Try avoiding the need of constant interfaces (interfaces that declare only constants). Aim to declare the constants in the class where they are most relevant to, if it needs to be shared, by other classes, start with declaring them as protected/default and only if it is really needed or conceptually makes sense declare them public.
- Here is the order in which a class constructs should be declared:
- private static final fields
- public static final fields
- private fields (static or non-static)
- public fields
- constructors (ordered by their number of arguments, starting with the one that accepts the least)
- public methods
- abstract methods (public or protected)
- setters/getters
- protected helper methods
- private helper methods
- The use of separators to clearly separate the class to sections:
- ======================== Setter/Getter ===============================
- ======================== Helper methods ==============================
- ======================== Inner Classes ===============================
Javadoc
Writing good documentation is hard. This is because it is hard to define how good documentation looks like. Everybody has their own way of writing. A good book on this subject is "The Elements of Java Style", which is recommended reading. Here are some general guidelines to writing good documentation:
- The documentation should at the very least cover the API. This means class level documentation and public and protected methods should be documented. Argueably private methods should be documented as well. This depends on the complexity of the method however.
- It should NOT be a step-by-step description of the implementation. The client is only interested in *what* the method accomplishes, possibly *why* it does what it does and the contract it needs to adhere to. Too much implementation details makes the documentation hard to comprehend.
- One of the most important things of good javadoc (if not the most important) is pre- and post- conditions. Pre-/post-conditions declare what a method expects (regarding the containing instance state and the passed in arguments), and what the client should expect after the method returns.
- Sometimes you have relations/dependencies between different methods which can be regarded as the overall class contract. In this case the usage of the @see tag is quite helpful to refer to the related methods/fields/classes
- Try using @link as much as possible in the javadoc. It makes navigation very easy in the create javadoc and in the IDE as well.
- Always use @return and @param in method javadocs
- Using @throws is highly recommended as well!
- Always have a blank line between the body of the javadoc and the @x declarations
/**
* Explain here what is it that you are doing.
*
* @param arg The argument
* @return Just another number
*/
public int doIt(Object arg) {
return i.amDoingIt();
}
Java 5 features
Now that Java 5 is here, we should use it's features. I won't list all the features of Java 5,because there are plenty of tutorials out there, like http://java.sun.com/developer/technicalArticles/releases/j2se15/. However, I will give some pointers:
- Always use generics when possible. For example, when you know you are dealing with a List of Strings, define it as such: List<String>. Even if the accepted type is Object, use List<Object>
- Generics make it possible to use enhanced for loops (foreach) as well.
List<String> strings = new ArrayList<String>; for (String s : strings) { ... }
- Use enums when when dealing with static constants that should be grouped together
public enum StopLight { red, amber, green };
- Use varargs when you want to remain flexible in the amount of arguments that need to be passed in
public String concatenate(String ... args) { StringBuffer result = new StringBuffer(); for (String text : args) { result.append(text); } return result.toString(); }
- When writing thread safe code, use the new java 5 concurrency support - avoid using the synchronized keyword
- Do not use StringBuffer?. Use StringBuilder? instead.
- Sometimes it makes more sense to use the old fashioned for loop than the new one. An example:
Object[] array2 = new Object[array1.length];
for (int i=0; i<array1.length; i++) {
array2[i] = array1[i];
}
As opposed to:
Object[] array2 = new Object[array1.length];
int i = 0;
for (Object obj : array1) {
array2[i] = obj;
i++;
}
hashCode(), equals() and toString()
These 3 methods are often left unimplemented, until they are absolutely needed. That is a shame, because they improve the design of the code and make logging and tracing much easier. Refrain from using commons-utils/lang to construct these methods!
equals()
Equals always comes with hashCode(). If you implement equals(), you must implement hashCode(). The default implementation of equals() checks object identity (x == y). Often, this is not a suitable implementation. When you look at the Javadoc of equals() it is hard to comprehend its contract. That's not because it lacks information, it's because they tried to throw in as much abstract terms as possible. To clarify things, the contract of equals() states the following:
- The object must be equal to itself (this == other)
- The class of the object must equal the class of the other object.
- If both objects are equal they should remain equal as long as they are not modified. The same applies to non-equality.
- If object A is equal to object B, and object B is equal to object C, object A is automatically equal to object C.
- If the other object is null, both objects can never be equal to each other.
- If both objects are equal, they must have the same hashcode (however, the opposite is not true!)
Example equals():
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
final User user = (User) other;
// lastname is not a required property in this case
if (lastname != null && !lastName.equals(other.getLastName())) {
return false;
}
return firstName.equals(other.getFirstName();
}
hashCode()
This method returns an integer that defines the hash code used for hashing collections (e.g. HashMap?, HashSet?). Hashcode *always* comes with equals(). If you implement hashCode(), you *must* implement equals(). What is important is that this method must return the same value for objects that are equal to each other.
The contract states the following requirements:
- If an object is not modified so that the equals() method has a different outcome, hashCode() must return the same value for an object at any time it is invoked.
- Equal objects must produce the same hash code, but unequal objects can produce the same hash code as well!
Example hashCode():
public int hashCode() {
int hash = 7;
hash = 29 * hash + firstName.hashCode();
hash = 29 * hash + (lastName != null ? lastName.hashCode() : 0)
return hash;
}
toString()
The toString() method can help tremendously in the readability of logging statements and can sometimes be used directly in a user interface as well (instead of having to use a PropertyEditor? for example). There is no preferred way of implementing this method. However, once you do implement it, make it easy to read so that a human being can comprehend what data the object holds.
Attachments
- HIS.xml (2.0 kB) -
IntelliJ HIS Coding Conventions
, added by robl@jteam.nl on 05/16/08 19:58:23.
