39 votes

Utilisez Java lambda au lieu de 'if else'

Avec Java 8, j'ai ce code:

if(element.exist()){
    // Do something
}

Je veux convertir à lambda style,

element.ifExist(el -> {
    // Do something
});

avec un ifExist méthode comme ceci:

public void ifExist(Consumer<Element> consumer) {
    if (exist()) {
        consumer.accept(this);
    }
}

Mais maintenant j'ai d'autre cas, l'appel de:

element.ifExist(el -> {
    // Do something
}).ifNotExist(el -> {
    // Do something
});

Je peux écrire un similaire ifNotExist, et je veux qu'ils sont mutuellement exclusifs (si l' exist condition est vraie, il n'est pas nécessaire de vérifier ifNotExist, parce que parfois, la méthode exist() prend tellement la charge de travail pour vérifier), mais je dois toujours vérifier deux fois. Comment puis-je l'éviter?

Peut-être que le "exister" mot de permettre à quelqu'un de mal comprendre mon idée. Vous pouvez imaginer que j'ai aussi besoin de quelques méthodes:

 ifVisible()
 ifEmpty()
 ifHasAttribute()

Beaucoup de gens ont dit que c'est une mauvaise idée, mais:

Dans Java 8, nous pouvons utiliser lambda forEach au lieu d'un traditionnel for boucle. Dans la programmation de l' for et if sur deux contrôles des flux. Si nous pouvons utiliser une lambda pour un for boucle, pourquoi utiliser lambda pour if mauvaise idée?

for (Element element : list) {
    element.doSomething();
}

list.forEach(Element::doSomething);

Dans Java 8, il y a Optional avec ifPresent, semblable à mon idée de ifExist:

Optional<Elem> element = ...
element.ifPresent(el -> System.out.println("Present " + el);

Et sur la maintenance du code et de lisibilité, que pensez-vous si j'ai le code suivant avec beaucoup de répétition simple if clauses?

    if (e0.exist()) {
        e0.actionA();
    } else {
        e0.actionB();
    }

    if (e1.exist()) {
        e0.actionC();
    }

    if (e2.exist()) {
        e2.actionD();
    }

    if (e3.exist()) {
        e3.actionB();
    }

Comparez à:

    e0.ifExist(Element::actionA).ifNotExist(Element::actionB);
    e1.ifExist(Element::actionC);
    e2.ifExist(Element::actionD);
    e3.ifExist(Element::actionB);

Qui est le meilleur? Et, oups, avez-vous remarqué que dans la tradition if code clause, il y a une erreur:

if (e1.exist()) {
    e0.actionC(); // Actually e1
}

Je pense que si nous utilisons lambda, on peut éviter cette erreur!

39voto

Joop Eggen Points 30166

Comme il correspond presque mais pas vraiment à Facultatif , vous pourriez peut-être reconsidérer la logique:

Java 8 a une expressivité limitée:

 Optional<Elem> element = ...
element.ifPresent(el -> System.out.println("Present " + el);
System.out.println(element.orElse(DEFAULT_ELEM));
 

Ici, le map peut restreindre la vue sur l'élément:

 element.map(el -> el.mySpecialView()).ifPresent(System.out::println);
 

Java 9:

 element.ifPresentOrElse(el -> System.out.println("Present " + el,
                        () -> System.out.println("Not present"));
 

En général, les deux branches sont asymétriques.

21voto

Michael Points 20266

Cela s'appelle une «interface fluide» . Modifiez simplement le type de retour et return this; pour vous permettre de chaîner les méthodes:

 public MyClass ifExist(Consumer<Element> consumer) {
    if (exist()) {
        consumer.accept(this);
    }
    return this;
}

public MyClass ifNotExist(Consumer<Element> consumer) {
    if (!exist()) {
        consumer.accept(this);
    }
    return this;
}
 

Vous pourriez devenir un peu plus chic et retourner un type intermédiaire:

 interface Else<T>
{
    public void otherwise(Consumer<T> consumer); // 'else' is a keyword
}

class DefaultElse<T> implements Else<T>
{
    private final T item;

    DefaultElse(final T item) { this.item = item; }

    public void otherwise(Consumer<T> consumer)
    {
        consumer.accept(item);
    }
}

class NoopElse<T> implements Else<T>
{
    public void otherwise(Consumer<T> consumer) { }
}

public Else<MyClass> ifExist(Consumer<Element> consumer) {
    if (exist()) {
        consumer.accept(this);
        return new NoopElse<>();
    }
    return new DefaultElse<>(this);
}
 

Exemple d'utilisation:

 element.ifExist(el -> {
    //do something
})
.otherwise(el -> {
    //do something else
});
 

12voto

Eran Points 35360

Vous pouvez utiliser une seule méthode qui prend deux consommateurs:

 public void ifExistOrElse(Consumer<Element> ifExist, Consumer<Element> orElse) {
    if (exist()) {
        ifExist.accept(this);
    } else {
        orElse.accept(this);
    }
}
 

Appelez-le ensuite avec:

 element.ifExistOrElse(
  el -> {
    // Do something
  },
  el -> {
    // Do something else
  });
 

5voto

Andrew Tobilko Points 1283

Le problème

(1) Vous semblez mélanger les différents aspects du flux de contrôle et de domaine logique.

element.ifExist(() -> { ... }).otherElementMethod();
          ^                      ^
        control flow method     business logic method

(2) Il est difficile de savoir comment les méthodes après un contrôle de la méthode des flux (comme ifExist, ifNotExist) devrait se comporter. Devraient-ils toujours être exécuté ou être appelé qu'en vertu de la condition (similaire à l' ifExist)?

(3) Le nom de l' ifExist implique une exploitation du terminal, donc il n'y a rien à retour - void. Un bon exemple est - void ifPresent(Consumer) de Optional.

La solution

Je voudrais écrire un entièrement séparés de la classe qui serait indépendant de toute classe et de toute condition spécifique.

L'interface est simple et se compose de deux contextless de contrôle de flux de méthodes - ifTrue et ifFalse.

Il peut y avoir quelques façons de créer un Condition objet. J'ai écrit un statique méthode de fabrique de votre instance (par exemple, element) et l'état (par exemple, Element::exist).

public class Condition<E> {

    private final Predicate<E> condition;
    private final E operand;

    private Boolean result;

    private Condition(E operand, Predicate<E> condition) {
        this.condition = condition;
        this.operand = operand;
    }

    public static <E> Condition<E> of(E element, Predicate<E> condition) {
        return new Condition<>(element, condition);
    }

    public Condition<E> ifTrue(Consumer<E> consumer) {
        if (result == null)
            result = condition.test(operand);

        if (result)
            consumer.accept(operand);

        return this;
    }

    public Condition<E> ifFalse(Consumer<E> consumer) {
        if (result == null)
            result = condition.test(operand);

        if (!result)
            consumer.accept(operand);

        return this;
    }

    public E getOperand() {
        return operand;
    }

}

En outre, nous pouvons intégrer Condition en Element:

class Element {

    ...

    public Condition<Element> formCondition(Predicate<Element> condition) {
        return Condition.of(this, condition);
    }

}

Le modèle que j'ai fais la promotion est:

  • travailler avec un Element;
  • obtenir un Condition;
  • le contrôle du flux par l' Condition;
  • revenir à la page d' Element;
  • continuer à travailler avec l' Element.

Le résultat

L'obtention d'un Condition par Condition.of:

Element element = new Element();

Condition.of(element, Element::exist)
             .ifTrue(e -> { ... })
             .ifFalse(e -> { ... })
         .getOperand()
             .otherElementMethod();

L'obtention d'un Condition par Element#formCondition:

Element element = new Element();

element.formCondition(Element::exist)
           .ifTrue(e -> { ... })
           .ifFalse(e -> { ... })
       .getOperand()
           .otherElementMethod();

Mise à jour 1:

Pour d'autres méthodes d'essai, l'idée reste la même.

Element element = new Element();

element.formCondition(Element::isVisible);
element.formCondition(Element::isEmpty);
element.formCondition(e -> e.hasAttribute(ATTRIBUTE));

Mise à jour 2:

C'est une bonne raison pour repenser la conception du code. Aucun des 2 extraits est grande.

Imaginez-vous besoin d' actionC dans e0.exist(). Quelle serait la méthode de référence Element::actionA - il être changé?

Il serait retourné dans un lambda:

e0.ifExist(e -> { e.actionA(); e.actionC(); });

à moins que vous enveloppez - actionA et actionC dans une seule méthode (qui m'a l'air terrible):

e0.ifExist(Element::actionAAndC);

Le lambda est maintenant encore moins "lisibles" puis l' if a été.

e0.ifExist(e -> {
    e0.actionA();
    e0.actionC();
});

Mais comment bien des efforts que nous faisons pour le faire? Et combien d'efforts allons-nous nous occuper de tout cela?

if(e0.exist()) {
    e0.actionA();
    e0.actionC();
}

3voto

ag157 Points 39

Si vous effectuez une vérification simple sur un objet et l'exécution de certains états en fonction de l'état alors une approche possible serait d'avoir une Carte avec un Prédicat comme la clé et l'expression souhaitée en tant que valeur par exemple.

Map<Predicate<Integer>,Supplier<String>> ruleMap = new HashMap<Predicate<Integer>,Supplier<String>>(){{
put((i)-> i<10,()->"Less than 10!");
put((i)-> i<100,()->"Less than 100!");
put((i)-> i<1000,()->"Less than 1000!");
}};

Nous pourrions plus tard flux de la Carte ci-dessous pour obtenir la valeur lorsque le Prédicat retourne vrai qui pourrait remplacer tous les si/d'autre code

        ruleMap.keySet()
               .stream()
               .filter((keyCondition)->keyCondition.test(numItems,version))
               .findFirst()
               .ifPresent((e)-> System.out.print(ruleMap.get(e).get()));

Puisque nous sommes à l'aide de la méthode findFirst() est équivalent à if/else if /else if ......

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X