Typing
Java is a statically typed language so the compiler enforces most type safety by default. That said, there are still patterns and pitfalls worth standardizing around generics, casting, and nullability.
It is worth noting the verbage on some of these standards. Never/always should be followed however the usage of "preferred" or "rarely" means decisions are up to the programmer and should take some consideration. If a bad-practice of typing is required than it should be denoted in a PR description.
Casting
Rarely use explicit casting. Always guard casts with an instanceof check to avoid
ClassCastException at runtime.
// goodif (animal instanceof Dog dog) { // pattern matching instanceof (Java 16+) dog.bark();}// badDog dog = (Dog) animal; // will throw runtime exception if animal is not a DogGenerics
Never use raw types, always provide type parameters. Raw types disable compile-time type
checking and can cause ClassCastException at runtime.
List<String> names = new ArrayList<>(); // goodList names = new ArrayList(); // badWildcard types (?) should be used sparingly and only when the type genuinely doesn't matter
to the operation. Prefer bounded wildcards when you need flexibility.
public void printAll(List<? extends Shape> shapes) { ... } // ok — read-only, boundedpublic void processAll(List<?> items) { ... } // rarely — only if truly type-agnosticNullability
Never return null from a method when absence of a value is a valid outcome, use
Optional<T> instead. This makes the possibility of an absent value explicit in the type
signature.
public Optional<User> findUser(String id) { ... } // goodpublic User findUser(String id) { ... } // bad — null return is invisible to the callerAlways annotate with @Nullable when null is a valid value for a parameter or field.
Unannotated parameters and return types are assumed non-null.
public void process(@Nullable String input) { ... } // explicitly nullablepublic void process(String input) { ... } // assumed non-nullNever use Optional as a method parameter or field type, only as a return type.
public void process(Optional<String> name) { ... } // bad — use @Nullable or overloads insteadvar
var is acceptable for local variables when the inferred type is immediately obvious from
the right-hand side. Never use it when the type would be unclear to a reader.
var users = new ArrayList<User>(); // ok — type is obviousvar result = service.process(input); // bad — type is not clear without checking the methodNever use var for fields, method parameters, or return types. Only local variables.
Wrapper Types
Never use wrapper types (Integer, Boolean, Long, etc.) for local variables or method
parameters unless required (e.g. a collection element type or an explicitly nullable value).
Prefer primitives.
int count = 0; // goodInteger count = 0; // bad — unnecessary boxingList<Integer> ids; // good — collections require wrapper typesEnums Over Stringly-Typed Values
Never use String or int constants to represent a fixed set of values. Always use enums,
which are type-safe and checked at compile time.
// goodenum Status { PENDING, ACTIVE, CLOSED }Status s = Status.ACTIVE;// badstatic final String STATUS_ACTIVE = "active";String s = STATUS_ACTIVE; // nothing stops passing any arbitrary string