Reducing if-else statements in Java - java

I have the following code:
void f(String t)
{
if(t.equals("a"))
{
someObject.setType(ObjectType.TYPE_A);
}
else if(t.equals("b"))
{
someObject.setType(ObjectType.TYPE_B);
}
// 50 more similar code
}
Is there any simple way to rewrite the if-else condition so as not to have that much code?

You should use something to eliminate the repetition of someObject.setType(ObjectType....)) If ObjectType is an enum, then write a method there similar to valueOf that will achieve that. See if you like this kind of solution:
void f(String t) { someObject.setType(ObjectType.byName(t)); }
enum ObjectType {
TYPE_A, TYPE_B;
public static ObjectType byName(String name) {
return valueOf("TYPE_" + name.toUpperCase());
}
}

Use a Map (which you'll have to populate) that maps from String to whatever type your ObjectType.TYPE_x values are.

I would add this as a functionality of the enum:
public enum ObjectType {
TYPE_A("a"),
TYPE_B("b");
private String stringType;
private ObjectType(String stringType) {
this.stringType = stringType;
}
public String getStringType() {
return this.stringType;
}
public static ObjectType fromStringType(String s) {
for (ObjectType type : ObjectType.values()) {
if (type.stringType.equals(s)) {
return type;
}
}
throw new IllegalArgumentException("No ObjectType with stringType " + s);
}
}
...
void f(String t) {
someObject.setType(ObjectType.fromStringType(t));
}

If you can refactor t into a char, you could use switch instead (Java 6):
void f(char t) {
switch(t) {
case 'a`:
someObject.setType(ObjectType.TYPE_A);
break;
case 'b':
someObject.setType(ObjectType.TYPE_B);
break;
// ...
}
}
As Marko pointed out, you could go with String too in Java 7.
It isn't that much shorter, but more elegant. Moreover, I think it might be faster too, as switch works close to O(1) with jump tables (Can somebody confirm whether this is true?), whether a number of if statements is O(n).
Fore more complex implementations than just a single setType you might think of a State Pattern implementation too.

1.You can go for Switch statement if you have number of if conditions more than 3.
2.you can convert your if else statements to ternary operations

The other suggestions are great - particularly smarter enums and maps. But the first most basic refactoring I would tackle here is to extract a method to return the enum directly and have the caller perform nothing more than the setType to that method's return value.
void f(String t) {
final ObjectType type = findType(t);
if (type != null)
someObject.setType(type);
}
ObjectType findType(String t) {
if (t.equals("a")) return ObjectType.TYPE_A;
if (t.equals("b")) return ObjectType.TYPE_B;
// 50 more similar code
}
In some cases this will be sufficient in and of itself; in others the findType() method may lead you to a simple map- or enum-based solution.

Related

Java 10 ifPresentOrElse that return boolean

I am a little confused on "how to do this properly":
// return true: if present and number of lines != 0
boolean isValid(Optional<File> optFile) {
return optFile.ifPresentOrElse(f -> return !isZeroLine(f), return false);
}
private boolean isZeroLine(File f) {
return MyFileUtils.getNbLinesByFile(f) == 0;
}
I know the syntax is not correct and not compiling, but it's just for you to get the idea.
How can I turn this into 'clean code'?
i.e. avoid doing:
if (optFile.isPresent()) {//} else {//}
Dealing with boolean return type(easily inferred Predicates), one way to do that could be to use Optional.filter :
boolean isValid(Optional<File> optFile) {
return optFile.filter(this::isZeroLine).isPresent();
}
But, then using Optionals arguments seems to be a poor practice. As suggested in comments by Carlos as well, another way of implementing it could possibly be:
boolean isValid(File optFile) {
return Optional.ofNullable(optFile).map(this::isZeroLine).orElse(false);
}
On another note, ifPresentOrElse is a construct to be used while performing some actions corresponding to the presence of the Optional value something like :
optFile.ifPresentOrElse(this::doWork, this::doNothing)
where the corresponding actions could be -
private void doWork(File f){
// do some work with the file
}
private void doNothing() {
// do some other actions
}

Can I use the Conditional Operators in a non-assignment situation in JAVA?

In case of assignment the situation is simple,
result = testCondition ? value1 : value2;
But what if I want to use it instead of an if statement?
for instance in a logging situation:
logger.shouldDebbug ? logger.log("logging") : (what to do if not?);
In the case I don't what to do anything in the case of false, can I still use this Operator?
Yes you can if you wrap them in a returning function, but no you shouldn't.
In your example of the logger, let your logger output to void, discard the input when debugging isn't enabled.
You do not want to riddle your code with all these logging checks.
Perform a check as least and as central as possible.
Either have a check in the logger.log function if debugging is enabled, or replace the logger with a dummy mock that does nothing except accept input and immediately discard it.
If you use standard logging frameworks like log4j you can set debugging levels, where you show only info or more serious, only warnings or more serious, only errors or more serious.
The same goes for other "quick" checks. If you find yourself using a certain pattern a lot, write a utility class for it with a static method if need be, so you have one place, where you have to change stuff, instead of 200 code points that you have to update when going to production.
You could use it if you insist, by defining a meaningless variable and take advantage of the functions' side-effects, but that's not a very good coding habit. It's purely a work-around.
For example:
public static boolean test() {
return 1>0;
}
public static int success() {
System.out.println("true");
return 0; // has no meaning whatsoever
}
public static int fail() {
System.out.println("false");
return 0; // has no meaning whatsoever
}
public static void main(String[] args) {
int meaningless = test() ? success() : fail();
}
Everything has been explained in comments, so I will put here only some idea:
public class Ternary {
private final boolean condition;
private Ternary(boolean condition) { this.condition = condition; }
public static Ternary of(boolean condition) { return new Ternary(condition); }
public Ternary onTrue(Runnable r) { if (condition) { r.run(); } return this; }
public Ternary onFalse(Runnable r) { if (!condition) { r.run(); } return this; }
}
Example of usage:
Ternary.of(o != null).onTrue(() -> doSomething()).onFalse(() -> doSomethingElse());
But simplier would be to write:
if (o != null) { doSomething(); } else { doSomethingElse(); }

Can we combine two methods that differ largely based on type?

I have two similar, but of different types, blocks of code in Java:
private Integer readInteger() {
Integer value = null;
while (value == null) {
if (scanner.hasNextInt()) {
value = scanner.nextInt();
} else {
scanner.next();
}
}
return value;
}
private Double readDouble() {
Double value = null;
while (value == null) {
if (scanner.hasNextDouble()) {
value = scanner.nextDouble();
} else {
scanner.next();
}
}
return value;
}
Is it possible to make just one method which would work for both of them?
I'd say, use a generic method, combined with the functional interfaces introduced in Java 8.
The method read now becomes a higher order function.
private <T> T read(Predicate<Scanner> hasVal, Function<Scanner, T> nextVal) {
T value = null;
while (value == null) {
if (hasVal.test(scanner)) {
value = nextVal.apply(scanner);
} else {
scanner.next();
}
}
return value;
}
Calling code becomes:
read(Scanner::hasNextInt, Scanner::nextInt);
read(Scanner::hasNextDouble, Scanner::nextDouble);
read(Scanner::hasNextFloat, Scanner::nextFloat);
// ...
So the readInteger() method can be adapted as follows:
private Integer readInteger() {
return read(Scanner::hasNextInt, Scanner::nextInt);
}
You could have something with three methods:
One which says if there is a value of the right type
Another which gets the value of the right type.
Another which discards whatever token you have.
For example:
interface Frobnitz<T> {
boolean has();
T get();
void discard();
}
You can pass this into your method:
private <T> T read(Frobnitz<? extends T> frob) {
T value = null;
while (value == null) {
if (frob.has()) {
value = frob.get();
} else {
frob.discard();
}
}
return value;
}
And then just implement Frobnitz for your Double and Integer cases.
To be honest, I'm not sure this gets you very much, especially if you've only got two cases; I'd be inclined just to suck up the small amount of duplication.
A lot of people have answered that you can use generics, but you can also simply remove the readInteger method, and only use the readDouble, as integers can be converted to doubles without data loss.
This is about code duplication.
The general approach is to turn similar code (you have) into equal code that can be extracted to a common parameterized method.
In your case what make the two code snipped differ is the access to methods of Scanner. You have to encapsulate them somehow. I'd suggest to do this with Java8 Functional interfaces like this:
#FunctionalInterface
interface ScannerNext{
boolean hasNext(Scanner scanner);
}
#FunctionalInterface
interface ScannerValue{
Number getNext(Scanner scanner);
}
Then replace the actual invocation of methods in scanner with the functional interface:
private Integer readInteger() {
ScannerNext scannerNext = (sc)->sc.hasNextInt();
ScannerValue scannerValue = (sc)-> sc.nextInt();
Integer value = null;
while (value == null) {
if (scannerNext.hasNext(scanner)) {
value = scannerValue.getNext(scanner);
} else {
scanner.next();
}
}
return value;
}
There is one more problem that the type of the value variable differs. So we replace it with its common supertype:
private Integer readInteger() {
ScannerNext scannerNext = (sc)->sc.hasNextInt();
ScannerValue scannerValue = (sc)-> sc.nextInt();
Number value = null;
while (value == null) {
if (scannerNext.hasNext(scanner)) {
value = scannerValue.getNext(scanner);
} else {
scanner.next();
}
}
return (Integer)value;
}
Now you have to places with a big equal section. You can select one of those sections starting with Number value = null; ending with the } before return ... and invoke your IDEs automated refactoring extract method:
private Number readNumber(ScannerNext scannerNext, ScannerValue scannerValue) {
Number value = null;
while (value == null) {
if (scannerNext.hasNext(scanner)) {
value = scannerValue.getNext(scanner);
} else {
scanner.next();
}
}
return value;
}
private Integer readInteger() {
return (Integer) readNumber( (sc)->sc.hasNextInt(), (sc)-> sc.nextInt());
}
private Double readDouble() {
return (Double) readNumber( (sc)->sc.hasNextDouble(), (sc)-> sc.nextDouble());
}
Comments argue against the use of custom interfaces against predefined interfaces from the JVM.
But my point in this answer was how to turn similar code into equal code so that it can be extracted to a single method rather that giving a concrete solution for this random problem.
Not an ideal solution but it still achieves the necessary removal of duplicate code and has the added benefit of not requiring Java-8.
// This could be done better.
static final Scanner scanner = new Scanner(System.in);
enum Read{
Int {
#Override
boolean hasNext() {
return scanner.hasNextInt();
}
#Override
<T> T next() {
return (T)Integer.valueOf(scanner.nextInt());
}
},
Dbl{
#Override
boolean hasNext() {
return scanner.hasNextDouble();
}
#Override
<T> T next() {
return (T)Double.valueOf(scanner.nextDouble());
}
};
abstract boolean hasNext();
abstract <T> T next();
// All share this method.
public <T> T read() {
T v = null;
while (v == null) {
if ( hasNext() ) {
v = next();
} else {
scanner.next();
}
}
return v;
}
}
public void test(String[] args) {
Integer i = Read.Int.read();
Double d = Read.Dbl.read();
}
There are some minor issues with this such as the casting but it should be a reasonable option.
A totally different approach from my other answer (and the other answers): don't use generics, but instead just write the methods more concisely, so you don't really notice the duplication.
TL;DR: rewrite the methods as
while (!scanner.hasNextX()) scanner.next();
return scanner.nextX();
The overall goal - write it as a single method - is only possible if you accept some amount of additional cruft.
Java method signatures do not take into account the return type, so it's not possible to have a next() method return an Integer in one context, and Double in another (short of returning a common supertype).
As such, you have to have something at the call sites to distinguish these cases:
You might consider passing something like Integer.class or Double.class. This does have the advantage that you can use generics to know that the returned value matches that type. But callers could pass in something else: how would you handle Long.class, or String.class? Either you need to handle everything, or you fail at runtime (not a good option). Even with a tighter bound (e.g. Class<? extends Number>), you still need to handle more than Integer and Double.
(Not to mention that writing Integer.class and Double.class everywhere is really verbose)
You might consider doing something like #Ward's answer (which I do like, BTW: if you're going to do it with generics, do it like that), and pass in functional objects which are able to deal with the type of interest, as well as providing the type information to indicate the return type.
But, again, you've got to pass these functional objects in at each call site, which is really verbose.
In taking either of these approaches, you can add helper methods which pass the appropriate parameters to the "generic" read method. But this feels like a backwards step: instead of reducing the number of methods to 1, it's increased to 3.
Additionally, you now have to distinguish these helper methods somehow at the call sites, in order to be able to call the appropriate one:
You could have overloads with a parameter of value type, rather than class type, e.g.
Double read(Double d)
Integer read(Integer d)
and then call like Double d = read(0.0); Integer i = read(0);. But anybody reading this code is going to be left wondering what that magic number in the code is - is there any significance to the 0?
Or, easier, just call the two overloads something different:
Double readDouble()
Integer readInteger()
This is nice and easy: whilst it's slightly more verbose than read(0.0), it's readable; and it's way more concise that read(Double.class).
So, this has got us back to the method signatures in OP's code. But this hopefully justifies why you still want to keep those two methods. Now to address the contents of the methods:
Because Scanner.nextX() doesn't return null values, the method can be rewritten as:
while (!scanner.hasNextX()) scanner.next();
return scanner.nextX();
So, it's really easy to duplicate this for the two cases:
private Integer readInteger() {
while (!scanner.hasNextInt()) scanner.next();
return scanner.nextInt();
}
private Double readDouble() {
while (!scanner.hasNextDouble()) scanner.next();
return scanner.nextDouble();
}
If you want, you could pull out a method dropUntil(Predicate<Scanner>) method to avoid duplicating the loop, but I'm not convinced it really saves you that much.
A single (near-)duplicated line is way less burdensome in your code than all those generics and functional parameters. It's just plain old code, which happens to be more concise (and, likely, more efficient) than "new" ways to write it.
The other advantage of this approach is that you don't have to use boxed types - you can make the methods return int and double, and not have to pay the boxing tax unless you actually need it.
This may not be of advantage to OP, since the original methods do return the boxed type; I don't know if this is genuinely desired, or merely an artefact of the way the loop was written. However, it is useful in general not to create those objects unless you really need them.
Reflection is an alternative if you don't care about performance.
private <T> T read(String type) throws Exception {
Method readNext = Scanner.class.getMethod("next" + type);
Method hasNext = Scanner.class.getMethod("hasNext" + type);
T value = null;
while (value == null) {
if ((Boolean) hasNext.invoke(scanner)) {
value = (T) readNext.invoke(scanner);
} else {
scanner.next();
}
}
return value;
}
Then you call
Integer i = read("Int");

How to pass different enums as argument?

I have some enums, all different, and I want to create a function that can find or not if a string is one of enum variable name (not sure it's really understandable).
enum MYENUM {
ONE,
TWO;
}
enum MYENUM1 {
RED,
GREEN;
}
I want to do this (this is just for the example, my enum are more complicated):
if(isInEnum(MYENUM, "one")) ...
if(isInEnum(MYENUM1, "one")) ...
isinEnum function (the code is bad, it's just for understanding):
boolean isinEnum(enum enumeration, String search) {
for(enum en : enumeration.values()){
if(en.name().equalsIgnoreCase(search)) return true;
}
return false;
}
Is this kind of thing possible?
I think not, according to what I can read on the web, but maybe someone has a solution to do this, instead of making one loop for each enum.
Here is a way using reflection...
public class EnumFinder {
public static <T extends Enum<T>> boolean isInEnum(Class<T> clazz, String name) {
for (T e : clazz.getEnumConstants()) {
if (e.name().equalsIgnoreCase(name)) {
return true;
}
}
return false;
}
public static void main(String[] argv) {
System.out.println(isInEnum(MYENUM.class, "one")); // true
System.out.println(isInEnum(MYENUM1.class, "one")); // false
}
}
Your attempt in your answer was actually very close. The only difference is that Java needs an instance of the defining class in order to answers questions about an unknown type dynamically at runtime.
This may not be the cleanest solution because it uses exceptions in the normal program flow, but it is certainly short, because it avoids the loop:
boolean isinEnum(Class<T> enumClass, String search) {
try {
Enum.valueOf(enumClass, search);
return true;
} catch (IllegalArgumentException iae) {
return false;
}
}
Your question is a bit tough to understand, but I think I get the gist of it.
You might be making things a lot tougher on yourself than necessary.
Take a look at Java's map interface / data structure (in java.util) and see if that moves you closer to your solution:
java.util Interface Map<K,V>
If not, repost with any leg-work you've done and I'll see if I can help ya' further. ;-)

Java in operator

For the one millionth time, I would have liked to use an IN operator in Java, similar to the IN operator in SQL. It could just be implemented as compiler syntactic sugar. So this
if (value in (a, b, c)) {
}
else if (value in (d, e)) {
}
...would really be awesome. In fact, the above is the same as the rather verbose (and not adapted for primitives) construct here:
if (Arrays.asList(a, b, c).contains(value)) {
}
else if (Arrays.asList(d, e).contains(value)) {
}
Or like this for int, long and similar types:
switch (value) {
case a:
case b:
case c:
// ..
break;
case d:
case e:
// ..
break;
}
Or maybe there could be even more efficient implementations.
Question:
Is something like this going to be part of Java 8? How can I make such a suggestion, if not? Or is there any equivalent construct that I could use right now?
You can write a helper method to do it.
public static <T> boolean isIn(T t, T... ts) {
for(T t2: ts)
if (t.equals(t2)) return true;
return false;
}
// later
if (isIn(value, a,b,c)) {
} else if (isIn(value, d,e)) {
}
Using op4j:
Op.onListFor(a,b,c).get().contains(value);
Using the same approach, you could create a helper classes Is with a method in:
class Is<T> {
private T value;
public Is( T value ) { this.value = value; }
public boolean in( T... set ) {
for( T item : set ) {
if( value.equals( item ) ) {
return true;
}
}
return false;
}
public static <T> Is<T> is( T value ) {
return new Is<T>( value );
}
}
with a static import, you can write:
if(is(value).in(a,b,c)) {
}
There has been a very old proposal for collection literals.
Currently there is Sets.newHashSet in Guava which is pretty similar to Arrays.asList.
You are looking for the Java Community Process
I doubt something like an IN operator would be made available, as there are already multiple ways of doing this(like using switch) as you yourself pointed out.
And I think requirement list for project-coin and J8 is already fully loaded to be anything like this to be considered.

Categories

Resources