170 lines
9.5 KiB
Markdown
170 lines
9.5 KiB
Markdown
---
|
||
title: 10 Core Java Best Practices with an industry strength code sample
|
||
tags:
|
||
- IT/Development/Java
|
||
---
|
||
|
||
Home › 13 Technical Key Areas Interview Q&A › Best Practice › 10 Core Java Best Practices with an industry strength code sample
|
||
10 Core Java Best Practices with an industry strength code sample
|
||
Posted on October 8, 2014 by Arulkumaran — 2 Comments ↓
|
||
|
||
|
||
Best Practices is one of the key areas, and often you can impress your interviewers, peers, and code reviewers by applying the best practices to your code. If you are an interviewer, you can show a piece of code that is badly written, and ask for the recommendations as to how she/he will improve the code. This will reveal a lot about a candidate’s attitude and ability towards writing quality code.
|
||
|
||
Here is a sample class “BatchRunKey” that can be used as a key class to store data in Maps.
|
||
Q1. Can you list the best practices applied in the “BatchRunKey” class shown below?
|
||
|
||
```java
|
||
package com.myapp.model;
|
||
|
||
import com.myapp.ValuationTypeCd;
|
||
import java.util.Date;
|
||
import javax.annotation.concurrent.Immutable;
|
||
import org.apache.commons.lang.builder.CompareToBuilder;
|
||
import org.apache.commons.lang.builder.EqualsBuilder;
|
||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||
import org.apache.commons.lang.builder.ToStringStyle;
|
||
import org.slf4j.Logger;
|
||
import org.slf4j.LoggerFactory;
|
||
import org.springframework.util.Assert;
|
||
|
||
@Immutable public final class BatchRunKey implements Comparable<BatchRunKey> {
|
||
private static final Logger LOG = LoggerFactory.getLogger(BatchRunKey.class);
|
||
final private long batchId;
|
||
final private String entity;
|
||
final private Date valuationDate;
|
||
final ValuationTypeCd valuationType;
|
||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||
private BatchRunKey(long batchId, String entity, Date valuationDate, ValuationTypeCd valuationType) {
|
||
super();
|
||
this.batchId = batchId;
|
||
this.entity = entity;
|
||
this.valuationDate = valuationDate;
|
||
this.valuationType = valuationType;
|
||
}
|
||
public static BatchRunKey newInstance(long batchId, String entity, Date valuationDate, ValuationTypeCd valuationType) {
|
||
Assert.notNull(batchId);
|
||
Assert.notNull(entity);
|
||
Assert.notNull(valuationDate);
|
||
Assert.notNull(valuationType); return new BatchRunKey(batchId, entity, valuationDate, valuationType);
|
||
}
|
||
public long getBatchId() {
|
||
return batchId;
|
||
}
|
||
public String getEntity() {
|
||
return entity;
|
||
}
|
||
public Date getValuationDate() {
|
||
return new Date(valuationDate.getTime()); //defensive copy
|
||
}
|
||
public ValuationTypeCd getValuationType() {
|
||
return valuationType;
|
||
}
|
||
@Override public int hashCode() {
|
||
return new HashCodeBuilder().append(batchId).append(entity)
|
||
.append(valuationDate).append(valuationType)
|
||
.toHashCode();
|
||
}
|
||
@Override public boolean equals(final Object obj) {
|
||
if(obj == null) {
|
||
return false;
|
||
}
|
||
if (obj instanceof BatchRunKey) {
|
||
final BatchRunKey runKey = (BatchRunKey) obj;
|
||
return new EqualsBuilder().append(this.batchId, runKey.batchId)
|
||
.append(this.entity, runKey.entity)
|
||
.append(this.valuationDate, runKey.valuationDate)
|
||
.append(this.valuationType, runKey.valuationType)
|
||
.isEquals();
|
||
}
|
||
return false;
|
||
}
|
||
@Override public int compareTo(BatchRunKey runKey) {
|
||
return new CompareToBuilder().append(this.batchId, runKey.batchId)
|
||
.append(this.entity, runKey.entity)
|
||
.append(this.valuationDate, runKey.valuationDate)
|
||
.append(this.valuationType, runKey.valuationType)
|
||
.toComparison();
|
||
}
|
||
@Override public String toString() {
|
||
LOG.debug("Executing toString()");
|
||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||
}
|
||
}
|
||
```
|
||
|
||
The enum class for “ValuationTypeCd”
|
||
|
||
```java
|
||
package com.myapp.model;
|
||
public enum ValuationTypeCd { WEEKLY, MONTHLY,YEARLY; }
|
||
```
|
||
|
||
A1. The best practices applied in summary are
|
||
|
||
1. Make your objects immutable where possible as immutable classes are inherently thread safe and less error prone as the state cannot be changed once constructed. The “BatchRunKey” class is immutable as it cannot be modified once constructed by invoking the public “newInstance(…)” method for the following reasons:
|
||
|
||
The member variables are marked as final, hence once constructed cannot be reassigned.
|
||
No setter methods to mutate the object.
|
||
The conctructor access modifier is private, hence cannot be invoked from outside this class. It is only being invoked by the “newInstance(…)” factory method.
|
||
The “getValuationDate()” method creates a new instance of the date object to not allow the object reference to escape. If it escapes, the “valuationDate” can be mutated from outside. So, the date object is defensively copied, and the copied object is returned instead of returning “this.valuationDate”.
|
||
|
||
2. Don’t reinvent the wheel, and use proven third-party libraries. Apache commons library classes “EqualsBuilder”, “CompareToBuilder”, “HashCodeBuilder”, and “ToStringBuilder” are used here as opposed to reinventing the wheel by writing your own implementation logic. The code is also easier to read and understand. For example, if you have to write your own “hashCode() & equals()” logic, it will look something like
|
||
|
||
```java
|
||
@Override public int hashCode() {
|
||
final int prime = 31;
|
||
int result = 1;
|
||
result = prime * result + (int) (batchId ^ (batchId >>> 32));
|
||
result = prime * result + ((entity == null) ? 0 : entity.hashCode());
|
||
result = prime * result + ((valuationDate == null) ? 0 : valuationDate.hashCode());
|
||
result = prime * result + ((valuationType == null) ? 0 : valuationType.hashCode());
|
||
return result;
|
||
}
|
||
@Override public boolean equals(Object obj) {
|
||
if (this == obj) return true;
|
||
if (obj == null) return false;
|
||
if (getClass() != obj.getClass()) return false;
|
||
BatchRunKey other = (BatchRunKey) obj;
|
||
if (batchId != other.batchId) return false;
|
||
if (entity == null) {
|
||
if (other.entity != null) return false;
|
||
}
|
||
else if (!entity.equals(other.entity)) return false;
|
||
if (valuationDate == null) {
|
||
if (other.valuationDate != null) return false;
|
||
} else if (!valuationDate.equals(other.valuationDate)) return false;
|
||
if (valuationType != other.valuationType) return false; return true;
|
||
}
|
||
```
|
||
|
||
Yaak, harder to read compared to the original code with Apache library classes. The Apache XXXBuilder classes use the builder design pattern for better readability. From Java 7 onwards, you have the “java.util.Objects” class, which consists of static utility methods for operating on objects.
|
||
|
||
You can also make us of the Google Gauva library, Spring utility classes, etc to not reinvent the wheel in many different scenarios. The “Assert” class used in the “newInstance(…)” method to validate for null values is from the “org.springframework.util” package.
|
||
|
||
3. Using the Comparable interface to sort the objects naturally. The “compareTo(…)” method and the “equals(..)” methods are implemented by adhering to the contract that:
|
||
|
||
“compareTo only returns 0, if a call to equals on the same objects would return true”
|
||
|
||
The JavaDoc for “Comparable” states that:
|
||
|
||
“The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C. Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException even though e.equals(null) returns false.”
|
||
|
||
So, having the JavaDoc handy and referring to it whilst coding is another best practice .
|
||
|
||
4.The toString( ) method from the Object class is overriden as it is handy for logging and debugging purpose. If you don’t override it, you will only get the memory address of the object as opposed to getting more meanigful values of batchId, entity, valuationDate, and valuationType, which are handy for debugging and auditing.
|
||
|
||
5. Using log4j as opposed to System.out.println(……) . The reason is that unlike the log4j framework, with “System.out.println (….)” you cannot change log levels, turn it off, customize it by using different appenders, etc.
|
||
|
||
6. Enums are favored to static final variables. The “ValuationTypeCd” is an enum.
|
||
|
||
7. The @Override annotation is used to ensure that the method names are not misspelled. If you mis-spell the method name, you will get a compile-time error.
|
||
|
||
8. The Factory method is favored over constructors as they have more meaningful names like “newInstance()”, and can decide on certain criteria which instance to create/return, extendable with Open/Close principle, and more reusable/testable.
|
||
|
||
9. The “newInstance()” method is also implemented with the “fail-fast” approach. The “Assert.notNull(…)” checks ensure that null values are not passed by the caller. If a null value is passed, it throw a validation exception. It also acts as a documentation in terms of “batchId, entity, valuationDate, and valuationType” being mandatory and accepting only non-null values.
|
||
|
||
10. A Java package ” com.myapp.model” is used for organizing Java classes into namespaces. Packages ensure uniqueness and provide more meaning as you know that “BatchRunKey” is a model class.
|
||
You may also like
|