En Java, quand utiliser des méthodes d'instances privées dans les interfaces?

9

Depuis Java 9, les méthodes d'une interface peuvent être privées. Une méthode privée peut être statique ou une méthode d'instance. Comme les méthodes privées ne peuvent être utilisées que dans les méthodes de l'interface elle-même, leur utilisation se limite à être des méthodes auxiliaires pour les autres méthodes de l'interface.

Cay S. Horstmann, Core Java Volume I - Fondamentaux

Je comprends que nous pouvons mettre la fonctionnalité commune dans les méthodes privées et non la rendre accessible au public. Mais nous pouvons avoir ici deux types de méthodes privées:

  1. private
  2. private static

L'utilisation de private staticméthodes est compréhensible, mais quand devrions-nous utiliser des privateméthodes? Nous ne traitons pas des instances ici car il s'agit d'une interface, alors pourquoi créer des privateméthodes est-il autorisé? N'avons-nous pas besoin que de private staticméthodes?

sg7610
la source
Une interface peut inclure des méthodes que d'autres méthodes d'instance appellent, mais ne sont pas destinées à la consommation publique.
Dave Newton
2
Essayez d'appeler la privateméthode d'instance de l'interface dans la classe qui implémente l'interface.
Abra
1
Une telle méthode privée pourrait appeler d'autres méthodes à partir de l'interface, elles ne sont donc pas équivalentes ou remplaçables par des private staticméthodes.
Mark Rotteveel
méthodes par défaut peut
Maurice Perry

Réponses:

2

OK, une autre tentative de répondre aux questions de OP. Lorsque vous devez appeler une autre méthode non statique sur l'interface à partir d'une méthode privée, la méthode privée ne peut pas être statique. Par exemple, il y aurait une erreur de compilation si la méthode privée ci-dessous était statique:

public interface InterfaceWithMethods {
    public default void doSomething() {
        doSomethingCommon();
    }

    public default void doSomethingElse() {
        doSomethingCommon();
    }

    public void actuallyDoSomething();

    private void doSomethingCommon() {
        System.out.println("Do something first.");
        actuallyDoSomething();
    }
}
jingx
la source
Pourquoi est-ce pertinent? Vous pouvez également implémenter chaque méthode en tant que "défaut public". La question est de savoir pourquoi / avec quelle intention choisiriez-vous la mise en œuvre x ou y plutôt que z - pas comment.
Florian Salihovic
2
@FlorianSalihovic, vous choisissez non statique plutôt que statique lorsque vous devez appeler une autre méthode à partir de cette méthode privée. N'est-ce pas le pourquoi?
jingx
Vous posez la mauvaise question. La visibilité des méthodes est choisie pour réduire ou élargir les possibilités d'interaction entre les objets. Il est important que les développeurs qui communiquent entendent comment leur code doit / doit / peut être utilisé. Vous pouvez tout implémenter dans des méthodes statiques ou n'utiliser aucune méthode statique. La question est importante car nous devons réfléchir aux conséquences de l'accès à la fonctionnalité d'autres objets / classes, qui ne devraient pas du tout être accessibles.
Florian Salihovic
2
@FlorianSalihovic Mais comme j'ai appris des commentaires des gens, OP ne posait pas de question sur la visibilité ou quand utiliser statique vs non statique, mais demandait pourquoi les méthodes privées non statiques étaient même autorisées sur les interfaces alors que la statique privée semble suffisante. Ma réponse a fourni un cas d'utilisation où seule une méthode non statique fonctionnerait.
jingx
3

Les interfaces sont utilisées pour définir le comportement d'un objet. Cela signifie que toutes les méthodes de l'interface sont exposées. Lorsque vous utilisez des méthodes par défaut, nous pouvons fournir des implémentations standard des méthodes définies, offrant une réutilisation du code au-delà des limites de classe.

Dans certains cas, une fonctionnalité est requise (peut-être juste pour la réutilisation de code dans différentes méthodes par défaut ) mais ne doit pas être exposée car elle polluerait les espaces de noms de la classe / objet. C'est là que les méthodes par défaut privées sont utiles. Des exemples de méthodes par défaut privées peuvent être des usines, des validations ou la gestion des états par défaut.

package com.company;

import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class Main {

  public static void main(final String[] args) {
    var messages =
        List.of(
            MessageQueue.newSubject("Message 1"),
            MessageQueue.newTopic("Message 2"),
            MessageQueue.newTopic("Message 3"));
    final MessageQueueAdapter1 queue1 = () -> messages;
    inspectQueue(queue1);
    final MessageQueueAdapter2 queue2 = () -> messages;
    inspectQueue(queue2);
  }

  private static void inspectQueue(final MessageQueue queue) {
    final List<Message> messagesWithSubject = queue.getMessagesWithSubject();
    assert messagesWithSubject.size() == 1 : "expected one message with 'Subject'";
    final List<Message> messagesWithTopic = queue.getMessagesWithTopic();
    assert messagesWithTopic.size() == 2 : "expected two message with 'Topic'";
    assert !queue.getMessages().isEmpty() && 3 == queue.getMessages().size()
        : "expected three messages in total";
  }

  @FunctionalInterface
  interface Message {
    private static boolean isPrefixedBy(final String message, final String prefix) {
      return message != null && !message.isEmpty() && message.startsWith(prefix);
    }

    default boolean hasSubject() {
      return isPrefixedBy(this.getMessage(), MessageQueue.PREFIX_SUBJECT);
    }

    default boolean hasTopic() {
      return isPrefixedBy(this.getMessage(), MessageQueue.PREFIX_TOPIC);
    }

    String getMessage();
  }

  interface MessageQueue {
    String PREFIX_SUBJECT = "Subject: ";

    String PREFIX_TOPIC = "Topic: ";

    private static Message newMessage(final String message) {
      return () -> message;
    }

    static Message newSubject(final String message) {
      return newMessage(PREFIX_SUBJECT + message);
    }

    static Message newTopic(final String message) {
      return newMessage(PREFIX_TOPIC + message);
    }

    List<Message> getMessages();

    List<Message> getMessagesWithSubject();

    List<Message> getMessagesWithTopic();
  }

  @FunctionalInterface
  interface MessageQueueAdapter1 extends MessageQueue {
    private static List<Message> filterBy(
        final List<Message> messages, final Predicate<Message> predicate) {
      return messages.stream().filter(predicate).collect(Collectors.toList());
    }

    /** {@inheritDoc} */
    @Override
    default List<Message> getMessagesWithSubject() {
      return filterBy(this.getMessages(), Message::hasSubject);
    }

    /** {@inheritDoc} */
    @Override
    default List<Message> getMessagesWithTopic() {
      return filterBy(this.getMessages(), Message::hasTopic);
    }
  }

  @FunctionalInterface
  interface MessageQueueAdapter2 extends MessageQueue {
    private List<Message> filterBy(final Predicate<Message> predicate) {
      return this.getMessages().stream().filter(predicate).collect(Collectors.toList());
    }

    /** {@inheritDoc} */
    @Override
    default List<Message> getMessagesWithSubject() {
      return filterBy(Message::hasSubject);
    }

    /** {@inheritDoc} */
    @Override
    default List<Message> getMessagesWithTopic() {
      return filterBy(Message::hasTopic);
    }
  }
}
Florian Salihovic
la source