Java conventions¶
Refer to Google’s Java style guide for additional recommendations.
Practices¶
Banned features¶
These features are banned:
clone()
andCloneable
finalize()
System.gc()
- labels (e.g.
outer_loop: for ...
) notify()
andwait()
synchronized
methods (synchronize on blocks instead)- non-
final
or mutablestatic
fields (with very limited exceptions as needed)
Comments¶
Forgo comments or Javadoc elements that are superfluous or included out of habit or convention. Remove any text that is obvious, repetitious, unclear, or wrong.
Examples of good and bad comments:
- ✅
@return Result; -1 if {@code list} is empty
(edge case) - ✅
// skip sort because gatorx sorts incidentally
(not a mistake) - ✅
// yes, we really do want to add 1 here
(not a mistake) - ✅
// This block is a bit tricky: ...
(explain difficult code) - 🟨
// == Algorithm start ==
(usually better to split the method) - ❌
@return A String
(only out of habit/convention) - ❌
// set x equal to 5
(obvious) - ❌
created by Kerri Johnson
(superfluous to Git) - ❌
created on 2022-10-27
(superfluous to Git)
Exceptions¶
Both checked and unchecked exceptions are fine. Avoid throwing general types like RuntimeException
.
Constructors¶
As a general guideline, constructors should map arguments transparently to fields. If more complex functionality needs to happen to construct the object, move it to a factory, builder, or static factory method.
Optional types¶
Rationale
Although the Java developers may not have originally intended such widespread usage, it makes code much more clear and much more robust.
Don’t return null
or accept null
as an argument in public methods; use Optional<>
instead. null
is permitted in non-public code to improve performance.
Collections¶
Prefer collections to arrays unless doing so causes significant performance issues.
Immutability and records¶
Prefer immutable types, and use records for data-carrier-like classes. In general: Immutable classes must have only final
fields and should not allow modification (except by reflection); constructors should make defensive copies, and getters should return defensive copies or views. This may not always be the appropriate choice, such as in places where performance is paramount.
Getters, setters, and builder methods¶
Use getXx()
/setXx()
for mutable types but Xx()
for immutable types:
- For mutable types: name the getter
public double getAngle()
(get-style naming) - For immutable types: name the getter
public double angle()
(record-style naming)
Builder methods should follow the immutable convention (i.e. angle()
).
toString
¶
Tip
IntelliJ can do this for you. Use the StringJoiner
toString
template.
Classes should typically override toString
and use this template:
public class Claz {
private final String name;
private final List<String> items;
public Test(String name, List<String> items) {
this.name = name;
this.items = new ArrayList<>(items);
}
@Override
public String toString() {
return new StringJoiner(", ", Test.class.getSimpleName() + '[', "]")
.add("name='" + name + '\'')
.add("items=" + items)
.toString();
}
}
hashCode
¶
Tip
IntelliJ can do this for you. Use the default hashCode
template.
Most classes should override hashCode
and equals
. hashCode
should use this template:
public class Claz {
@Override
public int hashCode() {
return Objects.hash(field1, field2); // ...
}
}
equals
¶
Tip
IntelliJ can do this for you. Use the provided hashCode
and equals
templates.
equals()
may use either
- universal equality, where
equals()
returnsfalse
for incompatible types - multiversal equality, where
equals()
throws an exception for incompatible types
In some languages, type safety of equality can be checked by the compiler. Scala 3 has type-safe multiversal equality.
Use getClass()
to check type compatibility, not instanceof
. (For universal equality, only getClass()
makes sense – and for multiversal equality, there is no difference.) Additionally, subclasses of classes defining equals()
should never add data or state.
Universal equality¶
For universal equality, use this template:
public class Claz {
private final String name;
private final List<String> items;
@Override
public int hashCode() {
return Objects.hash(name, items);
}
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final var o = (Claz) obj;
return Objects.equals(name, o.name) && Objects.equals(items, o.items);
}
}
IntelliJ template for universal equality
Use this IntellJ template. Under Generate ➤ equals() and hashCode() ➤ … make a new template called universal.
equals()
template:
#parse("equalsHelper.vm")
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final var o = (${class.getName()}) obj;
return ##
#set($i = 0)
#foreach($field in $fields)
#if ($i > 0)
&& ##
#end
#set($i = $i + 1)
#if ($field.primitive)
#if ($field.double || $field.float)
#addDoubleFieldComparisonConditionDirect($field) ##
#else
#addPrimitiveFieldComparisonConditionDirect($field) ##
#end
#elseif ($field.enum)
#addPrimitiveFieldComparisonConditionDirect($field) ##
#elseif ($field.array)
java.util.Arrays.equals($field.accessor, obj.$field.accessor)##
#else
java.util.Objects.equals($field.accessor, obj.$field.accessor)##
#end
#end
;
}
hashCode()
template:
public int hashCode() {
#if (!$superHasHashCode && $fields.size()==1 && $fields[0].array)
return java.util.Arrays.hashCode($fields[0].accessor);
#else
#set($hasArrays = false)
#set($hasNoArrays = false)
#foreach($field in $fields)
#if ($field.array)
#set($hasArrays = true)
#else
#set($hasNoArrays = true)
#end
#end
#if (!$hasArrays)
return java.util.Objects.hash(##
#set($i = 0)
#if($superHasHashCode)
super.hashCode() ##
#set($i = 1)
#end
#foreach($field in $fields)
#if ($i > 0)
, ##
#end
$field.accessor ##
#set($i = $i + 1)
#end
);
#else
#set($resultName = $helper.getUniqueLocalVarName("result", $fields, $settings))
#set($resultAssigned = false)
#set($resultDeclarationCompleted = false)
int $resultName ##
#if($hasNoArrays)
= java.util.Objects.hash(##
#set($i = 0)
#if($superHasHashCode)
super.hashCode() ##
#set($i = 1)
#end
#foreach($field in $fields)
#if(!$field.array)
#if ($i > 0)
, ##
#end
$field.accessor ##
#set($i = $i + 1)
#end
#end
);
#set($resultAssigned = true)
#set($resultDeclarationCompleted = true)
#elseif($superHasHashCode)
= super.hashCode(); ##
#set($resultAssigned = true)
#set($resultDeclarationCompleted = true)
#end
#foreach($field in $fields)
#if($field.array)
#if ($resultDeclarationCompleted)
$resultName ##
#end
= ##
#if ($resultAssigned)
31 * $resultName + ##
#end
java.util.Arrays.hashCode($field.accessor);
#set($resultAssigned = true)
#set($resultDeclarationCompleted = true)
#end
#end
return $resultName;
#end
#end
}
Multiversal equality¶
For multiversal equality, use this template:
public class Claz {
private final String name;
private final List<String> items;
@Override
public final int hashCode() {
return Objects.hash(field1); // ...
}
@Override
public final boolean equals(final Object obj) {
if (o == this) {
return true;
}
if (o == null) {
return false;
}
if (getClass() != obj.getClass()) {
throw new IllegalArgumentException(
"Type " + obj.getClass().getName()
+ " is incompatible with "
+ getClass().getName()
);
}
final var o = (Claz) obj;
return Objects.equals(name, o.name) && Objects.equals(items, o.items);
}
}
IntelliJ template for multiversal equality
Use this IntellJ template. Under Generate ➤ equals() and hashCode() ➤ … make a new template called universal.
equals()
template:
#parse("equalsHelper.vm")
@Override
public final boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (null == obj) {
return false;
}
if (getClass() != obj.getClass()) {
throw new IllegalArgumentException(
"Type "
+ obj.getClass().getName()
+ " is incompatible with "
+ getClass().getName()
);
}
final var o = (${class.getName()}) obj;
return ##
#set($i = 0)
#foreach($field in $fields)
#if ($i > 0)
&& ##
#end
#set($i = $i + 1)
#if ($field.primitive)
#if ($field.double || $field.float)
#addDoubleFieldComparisonConditionDirect($field) ##
#else
#addPrimitiveFieldComparisonConditionDirect($field) ##
#end
#elseif ($field.enum)
#addPrimitiveFieldComparisonConditionDirect($field) ##
#elseif ($field.array)
java.util.Arrays.equals($field.accessor, obj.$field.accessor)##
#else
java.util.Objects.equals($field.accessor, obj.$field.accessor)##
#end
#end
;
}
hashCode()
template:
public int hashCode() {
#if (!$superHasHashCode && $fields.size()==1 && $fields[0].array)
return java.util.Arrays.hashCode($fields[0].accessor);
#else
#set($hasArrays = false)
#set($hasNoArrays = false)
#foreach($field in $fields)
#if ($field.array)
#set($hasArrays = true)
#else
#set($hasNoArrays = true)
#end
#end
#if (!$hasArrays)
return java.util.Objects.hash(##
#set($i = 0)
#if($superHasHashCode)
super.hashCode() ##
#set($i = 1)
#end
#foreach($field in $fields)
#if ($i > 0)
, ##
#end
$field.accessor ##
#set($i = $i + 1)
#end
);
#else
#set($resultName = $helper.getUniqueLocalVarName("result", $fields, $settings))
#set($resultAssigned = false)
#set($resultDeclarationCompleted = false)
int $resultName ##
#if($hasNoArrays)
= java.util.Objects.hash(##
#set($i = 0)
#if($superHasHashCode)
super.hashCode() ##
#set($i = 1)
#end
#foreach($field in $fields)
#if(!$field.array)
#if ($i > 0)
, ##
#end
$field.accessor ##
#set($i = $i + 1)
#end
#end
);
#set($resultAssigned = true)
#set($resultDeclarationCompleted = true)
#elseif($superHasHashCode)
= super.hashCode(); ##
#set($resultAssigned = true)
#set($resultDeclarationCompleted = true)
#end
#foreach($field in $fields)
#if($field.array)
#if ($resultDeclarationCompleted)
$resultName ##
#end
= ##
#if ($resultAssigned)
31 * $resultName + ##
#end
java.util.Arrays.hashCode($field.accessor);
#set($resultAssigned = true)
#set($resultDeclarationCompleted = true)
#end
#end
return $resultName;
#end
#end
}
compareTo
¶
Immutable classes should implement Comparable
and override compareTo
as long as it is reasonable. compareTo
should be marked final
.
switch
and pattern matching¶
Only use enhanced switch
statements.
Final¶
final
for variables, method arguments, catch
arguments, try
resources, etc., is optional. If a final
was already added, don’t remove it without cause.
Optional syntax¶
In general, omit any syntax elements that are not required. This includes any that have no effect at runtime, excluding comments and annotations.
This includes:
- Optional grouping parentheses
- Optional qualifiers
this
andsuper
- Same/inner/outer class names when accessing within the same file
- Unnecessary qualifiers, such as
abstract
on interface methods - Explicit
.toString()
Variable declarations¶
Declare variables when they are needed, not at the start of a block. Use var
if the type is either obvious or unimportant. In main
methods, use String... args
.
Formatting¶
Use prettier-java with: - 4 spaces for indentation - print width of 120
Miscellaneous¶
Comments:
Use //
for multiline comments instead of /* */
, unless the comment spans many lines (i.e. more than 20).
Encoding:
Write non-ASCII characters without escaping, except characters that are likely to confuse readers.
Numbers:
Always add .0
to floats (e.g. double x = 2.0 * pi
) and prefix with 0.
(e.g. double x = 0.001
). Digit grouping with _
is optional; use it only for amounts or quantities, not identifiers.
Naming¶
Follow Google’s Java naming conventions. Notably, treat acronyms as words – for example, CobolError
, not COBOLError
. You may alter this practice if needed to maintain consistency with an extant convention; in particular, for IO
(e.g. in IOException
). Name asynchronous methods (those that return a CompletableFuture
) with the suffix Async
; for example, calculateAsync()
.
Member ordering¶
Note
I don’t recommend sorting members retroactively because it can result in large diffs.
Tip
IntelliJ can do this for you. Import the IntelliJ formatter settings. To set manually, choose the default order and enable “keep getters and setters together”.
Sort members by the following rules. Note that these rules are similar to most existing conventions, including IntelliJ’s default.
static final
field- initializer
- field
- constructor
static
method- method
- getter/setter
equals
,hashCode
, andtoString
(in order)enum
,interface
,static class
,class
(in order)
Always pair associated getters and setters (getter first). Within each of the 10 types, sort by decreasing visibility. Optionally: Consider subsequently sorting methods so that callers are above callees in a breadth-first manner, or by another natural ordering.