📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.
✅ Some premium posts are free to read — no account needed. Follow me on Medium to stay updated and support my writing.
🎓 Top 10 Udemy Courses (Huge Discount): Explore My Udemy Courses — Learn through real-time, project-based development.
▶️ Subscribe to My YouTube Channel (172K+ subscribers): Java Guides on YouTube
In my previous article, we have discussed Java generics best practices.
1. Use enums instead of int constants
What is a problem with using int constants?
// int enum pattern - deficient!
public static final int APPLE_FUJI = 0;
public static final int APPLE_PIPPIN = 1;
public static final int APPLE_GRANNY_SMITH = 2;
public static final int ORANGE_NAVEL = 0;
public static final int ORANGE_TEMPLE = 1;
public static final int ORANGE_BLOOD = 2;
- No way of type safety and no convenience.
- The compiler won't complain if you pass an apple to a method that expects orange
- int enums are compile-time constants. They are compiled into the clients that use them.
- If the int associated with an enum constant is changed, its clients must be recompiled.
- no easy to translate int enum constants into printable strings (all you see is a number)
What is the solution?
public enum Apple {
FUJI, PIPPIN, GRANNY_SMITH
}
public enum Orange {
NAVEL, TEMPLE, BLOOD
}
- They're classes that export one instance for each enum constant via a public static final field.
- They are a generalization of singletons
- Enums provide compile-time type safety
- You can translate enums into printable strings by calling toString() method
- Provide implementations of Object methods
- Implement Comparable, Serializable
public enum Planet {
MERCURY(3.302e+23, 2.439e6),
VENUS(4.869e+24, 6.052e6),
EARTH(5.975e+24, 6.378e6),
MARS(6.419e+23, 3.393e6),
JUPITER(1.899e+27, 7.149e7),
SATURN(5.685e+26, 6.027e7),
URANUS(8.683e+25, 2.556e7),
NEPTUNE(1.024e+26, 2.477e7);
private final double mass;
private final double radius;
private final double surfaceGravity;
// universal gravitational constant in m^3 / kg s^2
private static final double G = 6.67300E-11;
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
surfaceGravity = G * mass / (radius * radius);
}
public double getMass() {
return mass;
}
public double getRadius() {
return radius;
}
public double getSurfaceGravity() {
return surfaceGravity;
}
public double surfaceweight(double mass) {
return mass * surfaceGravity;
}
private static void testWeight() {
double earthWeight = 175.0;
double mass = earthWeight / Planet.EARTH.getSurfaceGravity();
System.out.println("Your earth weight = " + earthWeight);
for(Planet p : Planet.values()) {
System.out.println("Your Weight on " + p.toString() + " is " + p.surfaceweight(mass));
}
}
public static void main(String[] args) {
testWeight();
}
}
Your earth weight = 175.0
Your Weight on MERCURY is 66.13367201195054
Your Weight on VENUS is 158.38392626475218
Your Weight on EARTH is 175.0
Your Weight on MARS is 66.43069946746834
Your Weight on JUPITER is 442.6939017602279
Your Weight on SATURN is 186.46496971259847
Your Weight on URANUS is 158.3497094835828
Your Weight on NEPTUNE is 198.84611594678634
2. Use instance fields instead of ordinals
What is the problem using ordinals?
// Abuse of ordinal to derive an associated value - DONT DO THIS
public enum Ensemble1 {
SOLO, DUET, TRIO, QUARTET, QUINTET,
SEXTET, SEPTET, OCTET, NONET, DECTET;
public int numMusicians() {
return ordinal() + 1;
}
}
What is the solution?
public enum Ensemble {
SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5),
SEXTET(6), SEPTET(7), OCTET(8), DOUBLE_QUARTET(8),
NONET(9), DECTET(10), TRIPLE_QUARTET(12);
private final int numMusicians;
Ensemble(int n) {
this.numMusicians = n;
}
public int numMusicians() {
return numMusicians;
}
}
3. Use EnumSet instead of bit fields
What is the problem using bit fields?
// Bit field enumeration constants - OBSOLETE!
public static class Text {
public static final int STYLE_BOLD = 1 << 0; // 1
public static final int STYLE_ITALIC = 1 << 1; // 2
public static final int STYLE_UNDERLINE = 1 << 2; // 4
public static final int STYLE_STRIKE_THROUGH = 1 << 3; // 8
public Text() {
}
// parameter is bitwise OR of zero or more STYLE_ constants
public void applyStyles(int styles) {
// hard to interpret a bit field
// no easy way to iterate over all of the elements represented by a bit field
}
}
text.applyStyles(STYLE_BOLD | STYLE_ITALIC);
What is the solution?
import java.util.EnumSet;
import java.util.Set;
public class ExampleEnumSet {
/** EnumSet - a modern replacement for bit fields. */
public static class TextEnumSet {
public enum Style {
BOLD, ITALIC, UNDERLINE, STRIKETHROUGH;
}
/** Any set can be passed but EnumSet is best. */
public void applyStyles(Set<Style> styles) {
}
}
public static void main(String[] args) {
TextEnumSet t2 = new TextEnumSet();
t2.applyStyles(EnumSet.of(TextEnumSet.Style.BOLD, TextEnumSet.Style.ITALIC));
}
}
4. Use EnumMap instead of ordinal indexing
import java.util.EnumMap;
import java.util.Map.Entry;
/**
* EnumMap Demonstration Example
* @author Ramesh Fadatare
*
*/
public class EnumMapExample {
enum Days {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;
}
public static void main(final String[] args) {
final EnumMap<Days, String> enumMap = new EnumMap<>(Days.class);
enumMap.put(Days.SUNDAY, "Its Sunday!!");
enumMap.put(Days.MONDAY, "Its Monday!!");
enumMap.put(Days.TUESDAY, "Its Tuesday!!");
enumMap.put(Days.WEDNESDAY, "Its Wednesday!!");
enumMap.put(Days.THURSDAY, "Its Thursday!!");
enumMap.put(Days.FRIDAY, "Its Friday!!");
enumMap.put(Days.SATURDAY, "Its Saturday!!");
for(final Entry<Days, String> entry : enumMap.entrySet()){
System.out.println(" Key -> " + entry.getKey().SUNDAY);
System.out.println("Value - >" + entry.getValue());
}
}
}
Key -> SUNDAY
Value - >Its Sunday!!
Key -> SUNDAY
Value - >Its Monday!!
Key -> SUNDAY
Value - >Its Tuesday!!
Key -> SUNDAY
Value - >Its Wednesday!!
Key -> SUNDAY
Value - >Its Thursday!!
Key -> SUNDAY
Value - >Its Friday!!
Key -> SUNDAY
Value - >Its Saturday!!
5. Emulate extensible enums with interfaces
6. Prefer annotations to name patterns
What is a problem with naming patterns?
- Suppose you accidentally have tsetSafetyOverride() method instead of testSafetyOverride() method. JUnit wouldn't complain, but wouldn't execute the test either leading to false security.
- No way to ensure they are used only in appropriate ways
- No good way to associate parameter values with program elements
What is the solution?
// Marker annotation type declaration
import java.lang.annotation.*;
/**
* Indicates that the annotated method is a test method.
* Use only on parameterless static methods
*
* @Retention means Test annotations should be retained at runtime
* @Target means the Test annotation is legal only on method declarations (not class, field declarations)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}
/**
* Here's an example of using the Test annotation.
* It's called MARKER ANNOTATION because it has no parameters
*/
public class TestDemo {
// Test should pass
@Test
public static void m1() {
}
public static void m2() {
}
// Test should fail
@Test
public static void m3() {
throw new RuntimeException("Boom");
}
public static void m4() {
}
// Invalid use of non-static method
@Test
public void m5() {
}
public static void m6() {
}
@Test
public static void m7() {
throw new RuntimeException("Crash");
}
public static void m8() {
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
Class<? extends Exception> value();
}
public class ExceptionTestDemo {
// Test will pass because this throws an ArithmeticException
@ExceptionTest(ArithmeticException.class)
public static void m1() {
int i = 0;
i = i / i;
}
// Test will fail because this throws an ArrayOutOfBounds exception
@ExceptionTest(ArithmeticException.class)
public static void m2() {
int[] a = new int[0];
int i = a[1];
}
// Test will fail because it doesnt throw an exception
@ExceptionTest(ArithmeticException.class)
public static void m3() {
}
}
7. Consistently use the Override annotation
What is a problem without using @Override annotation?
public class Bigram {
private final char first;
private final char second;
public Bigram(char first, char second) {
this.first = first;
this.second = second;
}
// The bug is here! equals(), if overrided correctly takes an Object as parameter
public boolean equals(Bigram b) {
return b.first == first && b.second == second;
}
public int hashCode() {
return 31 * first + second;
}
public static void main(String[] args) {
Set<Bigram> s = new HashSet<Bigram>();
for(int i = 0; i < 10 ; i++) {
for(char ch = 'a'; ch <= 'z'; ch++) {
s.add(new Bigram(ch, ch));
}
}
System.out.println(s.size()); // size is 260
}
}
260
What is the solution?
public class Bigram {
private final char first;
private final char second;
public Bigram(char first, char second) {
this.first = first;
this.second = second;
}
// The bug is here! equals(), if overrided correctly takes an Object as parameter
@Override
public boolean equals(Object b) {
Bigram bigram = (Bigram) b;
return bigram.first == first && bigram.second == second;
}
@Override
public int hashCode() {
return 31 * first + second;
}
public static void main(String[] args) {
Set<Bigram> s = new HashSet<Bigram>();
for(int i = 0; i < 10 ; i++) {
for(char ch = 'a'; ch <= 'z'; ch++) {
s.add(new Bigram(ch, ch));
}
}
System.out.println(s.size()); // size is 260
}
}
26
8. Use marker interfaces to define types
- Define a type that is implemented by instances of the marked class, while marker annotations do not
- Can be targeted more precisely.
*
* @see java.io.ObjectOutputStream
* @see java.io.ObjectInputStream
* @see java.io.ObjectOutput
* @see java.io.ObjectInput
* @see java.io.Externalizable
* @since JDK1.1
*/
public interface Serializable {
}
Comments
Post a Comment
Leave Comment