Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Doing Trinary logic with java.lang.Boolean and switch

java.lang.Boolean is perfect to handle trinary logic, because it can exactly have three states: Boolean.TRUE (it is the case that), Boolean.FALSE (it is not the case that) and null (we don't know what the case is). It would be a nice design to handle this using switch statements, eg. in this constructor:

public class URN {
private String value = null;    

public URN (String value, Boolean mode){
    switch (mode){
        case TRUE:
            if(!isValidURN(value))
                throw new MalformedURLException("The string could not be parsed.");
            this.value = value;
        break;
        case FALSE:
            this.value = value.concat(checkByteFor(value));
        break;
        case null:
            if(isValidURN(value))
                this.value = value;
            else
                this.value = value.concat(checkByteFor(value));
        break;
    }
    return;
}

Unluckily, Java doesn't allow that, complaining "Cannot switch on a value of type Boolean." Implementing this results in an obfuscated control flow and unnice code:

public URN (String value, Boolean mode){
    Boolean valid = null;
    if (!Boolean.FALSE.equals(mode)){
        valid = isValidURN(value);

        if (Boolean.TRUE.equals(mode) && !valid)
                throw new MalformedURLException("The string could not be parsed.");

        if(Boolean.TRUE.equals(valid)) {
            this.value = value;
            return;
    }   }

    this.value = value.concat(checkByteFor(value));
}

Doing it the nice way requires to implement an enum class (which, in real life, is more complex than in this example, because .equals() must be rewritten so that Trinary.NULL.equals(null) becomes true) and converting:

private enum Trinary {TRUE, FALSE, NULL};
public URN (String value, Boolean toConvert, String x){

    Trinary mode;
    if(toConvert == null)
        mode = Trinary.NULL;
    else
        mode = toConvert.equals(Boolean.TRUE) ? Trinary.TRUE : Trinary.FALSE;

    switch (mode){
        case TRUE:
            if(!isValidURN(value)) throw new MalformedURLException("The string could not be parsed.");
            this.value = value;
        break;
        case FALSE:
            this.value = value.concat(checkByteFor(value));
        break;
        case NULL:
            if(isValidURN(value))
                this.value = value;
            else
                this.value = value.concat(checkByteFor(value));
        break;
    }
    return;
}

To my eyes, this is the better since more readable solution, but the another half of the origin method size of code just to convert is annoying, and in real life you have to care about two different nulls with the same semantic. Is there a better way to do it?

like image 566
Matthias Ronge Avatar asked Feb 20 '23 13:02

Matthias Ronge


1 Answers

Using a null object to convey information like this is not optimal. Remember that you cannot do any method calls on a null object, which again would mean that if you in the future ever would want to call any .getClass, .equals, .compare etc., you would have to rewrite your code.

Your best option is definitely to go with the enum option.

enum Ternary {TRUE,FALSE,UNKNOWN}

You can furthermore expand the class to have a method of getting such object,

public Ternary getByValue(Boolean o) {
    if(o == null)
        return UNKNOWN;
    if(o)
        return TRUE;
    return FALSE;
}
like image 57
Jonas G. Drange Avatar answered Feb 23 '23 00:02

Jonas G. Drange