Reputation: 993
I have a method which checks for nulls. Is there a way to reduce the number of lines in the method? Currently, the code looks "dirty":
private int similarityCount (String one, String two) {
if (one == null && two == null) {
return 1;
} else if (one == null && two != null) {
return 2;
} else if (one != null && two == null) {
return 3;
} else {
if(isMatch(one, two))
return 4;
return 5;
}
}
Upvotes: 28
Views: 10037
Reputation: 1650
private int similarityCount (String one, String two) {
if (one == null && two == null) {
return 1;
}
if (one == null) {
return 2;
}
if (two == null) {
return 3;
}
if (isMatch(one, two)) {
return 4;
}
return 5;
}
Upvotes: 61
Reputation: 1
This should be slightly faster if neither is null, as it only performs one 'if' statement in this case.
private int similarityCount (String one, String two) {
if (one == null || two == null) { // Something is null
if (two != null) { // Only one is null
return 2;
}
if (one != null) { // Only two is null
return 3;
}
return 1; // Both must be null
}
return isMatch(one, two) ? 4 : 5;
}
Upvotes: 0
Reputation: 27529
You can create a pseudo-lookup-table. Some people frown on the nested ternary operators and it's highly dependent on whitespace for readability, but it can be a very readable approach to conditional returning:
private int similarityCount (String one, String two) {
return (one == null && two == null) ? 1
: (one == null && two != null) ? 2
: (one != null && two == null) ? 3
: isMatch(one, two) ? 4
: 5;
}
Upvotes: 5
Reputation: 2014
I like expressions.
private static int similarityCount (String one, String two) {
return one == null ?
similarityCountByTwoOnly(two) :
two == null ? 3 : (isMatch(one, two) ? 4 : 5)
;
}
private static int similarityCountByTwoOnly(String two) {
return two == null ? 1 : 2;
}
As an aside, I would probably challenge why you are doing this. I would assume you would do some kind of check on the returned integer after you've evaluated it and branch your logic based on it. If that is the case, you've just made a less readable check for null where the user of your method needs to understand the contract implicit in the value of the integer.
Also, here's a simple solution for when you need to check if strings are equal when they may be null:
boolean same = one == null ? two == null : one.equals(two);
Upvotes: 1
Reputation: 2107
Getting rid of IF statements is good fun. Using a Map is one way of doing this. It doesn't fit this case exactly because of the call to isMatch, but I offer it as an alternative which cuts the similarityCount method body to a single line with one IF
The following code has two IFs. If GetOrDefault didn't evaluate the second argument, it could be reduced to one. Unfortunately it does so the null check inside isMatch is necessary.
You could go a lot further with this if you wanted to. For example, isMatch could return 4 or 5 rather than a boolean which would help you simplify further.
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.util.Map;
public class SimilarityCount {
private Map<SimilarityCountKey, Integer> rtn = ImmutableMap.of(new SimilarityCountKey(null, null), 1, new SimilarityCountKey(null, ""), 2, new SimilarityCountKey("", null), 3);
public int similarityCount(String one, String two) {
return rtn.getOrDefault(new SimilarityCountKey(one, two), isMatch(one, two) ? 4 : 5);
}
private boolean isMatch(String one, String two) {
if (one == null || two == null) {
return false;
}
return one.equals(two);
}
private class SimilarityCountKey {
private final boolean one;
private final boolean two;
public SimilarityCountKey(String one, String two) {
this.one = one == null;
this.two = two == null;
}
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}
}
In case anyone else fancies a crack at another solution, here are some tests to help you get started
import org.junit.Assert;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
public class SimilarityCountTest {
@Test
public void one(){
Assert.assertThat(new SimilarityCount().similarityCount(null,null), is(1));
}
@Test
public void two(){
Assert.assertThat(new SimilarityCount().similarityCount(null,""), is(2));
}
@Test
public void three(){
Assert.assertThat(new SimilarityCount().similarityCount("",null), is(3));
}
@Test
public void four(){
Assert.assertThat(new SimilarityCount().similarityCount("",""), is(4));
}
@Test
public void five(){
Assert.assertThat(new SimilarityCount().similarityCount("a","b"), is(5));
}
}
Upvotes: 0
Reputation: 545558
Since the actual purpose of the function seems to be to handle non-null
objects by matching them, I’d handle all the null
checks in a guard statement at the beginning.
Then, once you’ve established that no argument is null
, you can handle the actual logic:
private int similarityCount(String a, String b) {
if (a == null || b == null) {
return a == b ? 1 : a == null ? 2 : 3;
}
return isMatch(a, b) ? 4 : 5;
}
This is both more concise and more readable than the other options.
That said, real functions wouldn’t usually return such numeric codes. Unless your method was simplified to exemplify the problem, I’d strongly urge you to reconsider the logic and instead write something akin to what follows:
private boolean similarityCount(String a, String b) {
if (a == null || b == null) {
throw new NullPointerException();
}
return isMatch(a, b);
}
Or:
private boolean similarityCount(String a, String b) {
if (a == null) {
throw new IllegalArgumentException("a");
}
if (b == null) {
throw new IllegalArgumentException("b");
}
return isMatch(a, b);
}
These approaches would be more conventional. On the flip side, they may trigger an exception. We can avoid this by returning a java.util.Optional<Boolean>
in Java 8:
private Optional<Boolean> similarityCount(String a, String b) {
if (a == null || b == null) {
return Optional.empty();
}
return Optional.of(isMatch(a, b));
}
At first glance this may seem to be no better than returning null
but optionals are in fact far superior.
Upvotes: 25
Reputation: 2692
It can be done in one line using Java conditional operator:
return (one==null?(two==null?1:2):(two==null?3:(isMatch(one,two)?4:5)));
Upvotes: 5
Reputation: 393781
I prefer nested conditions in such cases :
private int similarityCount (String one, String two) {
if (one==null) {
if (two==null) {
return 1;
} else {
return 2;
}
} else {
if (two==null) {
return 3;
} else {
return isMatch(one, two) ? 4 : 5;
}
}
}
Of course you can achieve a shorter version by using more ternary conditional operators.
private int similarityCount (String one, String two) {
if (one==null) {
return (two==null) ? 1 : 2;
} else {
return (two==null) ? 3 : isMatch(one, two) ? 4 : 5;
}
}
Or even (now this is getting less readable) :
private int similarityCount (String one, String two) {
return (one==null) ? ((two==null) ? 1 : 2) : ((two==null) ? 3 : isMatch(one, two) ? 4 : 5);
}
Upvotes: 33
Reputation: 13436
The code looks clear enough for me. You can make it shorter with nesting and ternary operators:
if(one==null) {
return two==null ? 1 : 2;
}
if(two==null) {
return 3;
}
return isMatch(one,two) ? 4 : 5;
Upvotes: 9