Wann sollten wir in Java private Instanzmethoden in Schnittstellen verwenden?

9

Ab Java 9 können Methoden in einer Schnittstelle privat sein. Eine private Methode kann eine statische oder eine Instanzmethode sein. Da private Methoden nur in den Methoden der Schnittstelle selbst verwendet werden können, beschränkt sich ihre Verwendung darauf, Hilfsmethoden für die anderen Methoden der Schnittstelle zu sein.

Cay S. Horstmann, Core Java Band I - Grundlagen

Ich verstehe, dass wir die gemeinsame Funktionalität in die privaten Methoden integrieren und nicht öffentlich zugänglich machen können. Aber wir können hier zwei Arten von privaten Methoden anwenden:

  1. private
  2. private static

Die Verwendung von private staticMethoden ist verständlich, aber wann sollten wir privateMethoden verwenden? Wir haben es hier nicht mit Instanzen zu tun, da dies eine Schnittstelle ist. Warum ist das Erstellen von privateMethoden zulässig? Brauchen wir nicht nur private staticMethoden?

sg7610
quelle
Eine Schnittstelle kann Methoden enthalten, die von anderen Instanzmethoden aufgerufen werden, jedoch nicht für den öffentlichen Gebrauch bestimmt sind.
Dave Newton
2
Versuchen Sie, die privateInstanzmethode der Schnittstelle in der Klasse aufzurufen, die die Schnittstelle implementiert.
Abra
1
Eine solche private Methode könnte andere Methoden über die Schnittstelle aufrufen, sodass sie nicht den private staticMethoden entsprechen oder durch diese ersetzt werden können.
Mark Rotteveel
Standardmethoden vielleicht
Maurice Perry

Antworten:

2

OK, ein weiterer Versuch, die Fragen von OP tatsächlich zu beantworten. Wenn Sie eine andere nicht statische Methode auf der Schnittstelle von einer privaten Methode aus aufrufen müssen, kann die private Methode nicht statisch sein. Zum Beispiel würde es einen Kompilierungsfehler geben, wenn die folgende private Methode statisch wäre:

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
quelle
Warum ist das relevant? Sie können jede Methode auch als "public default" implementieren. Die Frage ist, warum / mit welcher Absicht Sie die Implementierung x oder y anstelle von z wählen würden - nicht wie.
Florian Salihovic
2
@FlorianSalihovic Sie würden nicht statisch statt statisch wählen, wenn Sie eine andere Methode aus dieser privaten Methode aufrufen müssen. Ist das nicht der Grund?
Jingx
Sie stellen die falsche Frage. Die Sichtbarkeit von Methoden wird gewählt, um die Möglichkeiten der Interaktion von Objekten untereinander einzugrenzen oder zu erweitern. Es ist wichtig, dass Entwickler, die kommunizieren, beabsichtigen, wie ihr Code verwendet werden soll / muss / kann. Sie können alles in statischen Methoden implementieren oder überhaupt keine statische Methode verwenden. Die Frage ist wichtig, da wir über die Konsequenzen nachdenken müssen, wenn andere Objekte / Klassen auf Funktionen zugreifen, auf die überhaupt nicht zugegriffen werden sollte.
Florian Salihovic
2
@FlorianSalihovic Aber wie ich aus den Kommentaren der Leute erfahren habe, fragte OP nicht nach der Sichtbarkeit oder wann statische oder nicht statische Methoden verwendet werden sollten, sondern nach der Frage, warum nicht statische private Methoden überhaupt auf Schnittstellen zulässig sind, wenn private statische Methoden anscheinend ausreichen. Meine Antwort lieferte einen Anwendungsfall, in dem nur eine nicht statische Methode funktionieren würde.
Jingx
3

Schnittstellen werden verwendet, um das Verhalten eines Objekts zu definieren. Dies bedeutet, dass alle Methoden der Schnittstelle verfügbar gemacht werden. Bei Verwendung von Standardmethoden können wir Standardimplementierungen der definierten Methoden bereitstellen und die Wiederverwendung von Code über Klassengrenzen hinweg anbieten.

In einigen Fällen ist Funktionalität erforderlich (möglicherweise nur für die Wiederverwendung von Code in verschiedenen Standardmethoden ), sollte jedoch nicht verfügbar gemacht werden, da dies die Namespaces der Klasse / des Objekts verschmutzen würde. Hier bieten sich private Standardmethoden an. Beispiele für private Standardmethoden können Fabriken, Validierungen oder die Behandlung von Standardzuständen sein.

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
quelle