Declare Consumer and action with it in one line

1.5k views Asked by At

Experimenting with my code I was wondering is there any way to "compress" it more (one-liner is an ultimate goal).

vanilla java:

public void setUsingSwitch(Field field, String value) {
    switch (field) {
        case FIRST_NAME:
            setFirstName(value);
            break;
        case LAST_NAME:
            setLastName(value);
            break;
        default:
            throw new IndexOutOfBoundsException("Unknow field " + field);
    }
}

"compressed":

public void setUsingConsumer(Field field, String value) {
    Consumer<String> setter = field == FIRST_NAME ? this::setFirstName : field == LAST_NAME ? this::setLastName : v -> {
        throw new IndexOutOfBoundsException("Unknow field " + field);
    };
    setter.accept(value);
}

For both used:

public static enum Field { FIRST_NAME, LAST_NAME }

void setFirstName(String value) { }

void setLastName(String value) { }

Of course it is just scientific interest for me but is there any way to write even smaller code?

4

There are 4 answers

0
Tagir Valeev On BEST ANSWER

Here's an alternative approach which is not shorter, but sometimes preferable:

import java.util.function.BiConsumer;

public class MyObj {
    public static enum Field {
        FIRST_NAME(MyObj::setFirstName),
        LAST_NAME(MyObj::setLastName);

        Field(BiConsumer<MyObj, String> setter) {
            this.setter = setter;
        }

        final BiConsumer<MyObj, String> setter;
    }

    public void set(Field field, String value) {
        field.setter.accept(this, value);
    }

    public void setFirstName(String s) {...}
    public void setLastName(String s) {...}
}
0
Holger On

Since you revealed that you actually don’t have an enum, a more general solution may look like this:

class Person {
    public void setFirstName(String value) {
        // …
    }
    public void setLastName(String value) {
        // …
    }
    static final Map<String,BiConsumer<Person,String>> FIELDS;
    static {
        Map<String,BiConsumer<Person,String>> m=new HashMap<>();
        m.put("FirstName", Person::setFirstName);
        m.put("LastName", Person::setLastName);
        FIELDS=Collections.unmodifiableMap(m);
    }
    public void setField(String field, String value) {
        FIELDS.getOrDefault(field, (k,v)->{ throw new NoSuchElementException(); })
              .accept(this, value);
    }
}

By the way, since you said “business logic”, I recommend the following read: “Falsehoods Programmers Believe About Names

0
ytterrr On

Here's what I came to the end with Optional:

Field field = FIRST_NAME;

Optional.<Consumer<String>>ofNullable(
        field == FIRST_NAME
                ? this::setFirstName
                : field == LAST_NAME
                        ? this::setLastName
                        : null).orElseThrow(IndexOutOfBoundsException::new)
        .accept("John");

Although I'm not sure that writing it in one line is a good thing. Or even using it instead of simple switch.

0
Harmlezz On

Given your initial design, I guess you want to enum all possible setters for a Person. Further it seams as if you want to collect the Field to set and the value and afterwards call the proper setter. This is my solution preserving your design goals:

public enum Field {

    FIRST_NAME {
        public void setValue(String value, Person person) {
            person.setFirstName(value);
        }},

    LAST_NAME {
        public void setValue(String value, Person person) {
            person.setLastName(value);
        }};

    public abstract void setValue(String value, Person person);
}

public class Person {

    private String firstName;
    private String lastName;

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public void clearUsingConsumer(Field field, String value) {
        field.setValue(value, this);
    }
}