Findbugs is falsely reporting a circular dependency when a class implements the Builder pattern.
Here is a sample class that gets flagged as having a circular dependency between the User class and the Builder class.
import java.io.Serializable;
import java.util.UUID;
import jdk.nashorn.internal.ir.annotations.Immutable;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.cpicard.common.validation.EmailAddress;
/**
* A user represents any user of the system. This object is for tracking which user is tied to a specific object and is not
* intended to provide login validation.
*
* @author Chris Picard, chris.s.picard@gmail.com
* @version 1.0.0
*/
@Immutable
public final class User implements Serializable {
private static final long serialVersionUID = 1L;
private final UUID userId;
private final String firstName;
private final String lastName;
private final String emailAddress;
@SuppressWarnings("synthetic-access")
private User(final Builder builder) {
this.userId = builder.userId;
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.emailAddress = builder.emailAddress;
}
public UUID getUserId() {
return userId;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getEmailAddress() {
return emailAddress;
}
@Override
public int hashCode() {
final HashCodeBuilder builder = new HashCodeBuilder();
builder.append(userId);
builder.append(firstName);
builder.append(lastName);
builder.append(emailAddress);
return builder.toHashCode();
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final User other = (User) obj;
final EqualsBuilder builder = new EqualsBuilder();
builder.append(this.userId, other.userId);
builder.append(this.firstName, other.firstName);
builder.append(this.lastName, other.lastName);
builder.append(this.emailAddress, other.emailAddress);
return builder.isEquals();
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
/**
* Implementation of the builder pattern.
*
* @author Chris Picard, chris.s.picard@gmail.com
* @version 1.0.0
*/
public static final class Builder {
private UUID userId;
private String firstName;
private String lastName;
private String emailAddress;
public Builder setValues(final User user) {
this.userId = user.getUserId();
this.firstName = user.getFirstName();
this.lastName = user.getLastName();
this.emailAddress = user.getEmailAddress();
return this;
}
public Builder setUserId(final UUID userId) {
this.userId = userId;
return this;
}
public Builder setFirstName(final String firstName) {
this.firstName = firstName;
return this;
}
public Builder setLastName(final String lastName) {
this.lastName = lastName;
return this;
}
public Builder setEmailAddress(@EmailAddress final String emailAddress) {
this.emailAddress = emailAddress;
return this;
}
@SuppressWarnings("synthetic-access")
public User build() {
if (userId == null) {
userId = UUID.randomUUID();
}
if (StringUtils.isEmpty(firstName)) {
throw new IllegalStateException("First name is not set");
}
return new User(this);
}
}
}
I don't see any warnings issued by FindBugs in this code. Which version of FindBugs are you using? Please attach the resulting findbugs.xml report.
I am using the FindBugs Eclipse Plugin 3.0.0. Confidence level medium, min bug report 20, all categories and not hidden detectors enabled.
CD_CIRCULAR_DEPENDENCY is not a FindBugs bug pattern, it's issued by FB-contrib which is separate project not supported by our team. You should report this problem to FB-contrib developers, probably here or here.
Thanks for the clarification, I will keep in mind that they are a separate project int the future.