Kiedy w Javie powinniśmy korzystać z metod instancji prywatnych w interfejsach?

9

Począwszy od Java 9, metody w interfejsie mogą być prywatne. Metodą prywatną może być metoda statyczna lub instancja. Ponieważ prywatnych metod można używać tylko w metodach samego interfejsu, ich użycie jest ograniczone do metod pomocniczych dla innych metod interfejsu.

Cay S. Horstmann, Core Java Tom I - Podstawy

Rozumiem, że możemy umieścić wspólną funkcjonalność w metodach prywatnych i nie udostępniać jej publicznie. Ale możemy tu mieć dwa rodzaje prywatnych metod:

  1. private
  2. private static

Stosowanie private staticmetod jest zrozumiałe, ale kiedy powinniśmy stosować privatemetody? Nie mamy tutaj do czynienia z instancjami, ponieważ jest to interfejs, więc dlaczego tworzenie privatemetod jest dozwolone? Czy nie potrzebujemy tylko private staticmetod?

sg7610
źródło
Interfejs może zawierać metody wywoływane przez inne metody instancji, ale nie są przeznaczone do publicznego użytku.
Dave Newton,
2
Spróbuj wywołać privatemetodę instancji interfejsu w klasie, która implementuje interfejs.
Abra,
1
Taka prywatna metoda może wywoływać inne metody z interfejsu, więc nie są one równoważne ani zastępowane przez private staticmetody.
Mark Rotteveel,
być może domyślne metody
Maurice Perry

Odpowiedzi:

2

OK, kolejna próba odpowiedzi na pytania OP. Gdy trzeba wywołać inną metodę niestatyczną na interfejsie z metody prywatnej, metoda prywatna nie może być statyczna. Na przykład wystąpiłby błąd kompilacji, gdyby poniższa metoda prywatna była statyczna:

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
źródło
Dlaczego to ma znaczenie? Możesz także zaimplementować każdą metodę jako „publiczną domyślną”. Pytanie dotyczy tego, dlaczego / z jaką intencją wybrałbyś implementację x lub y zamiast z - nie jak.
Florian Salihovic
2
@FlorianSalihovic wybrałbyś opcję niestatyczną zamiast statycznej, gdy musisz wywołać inną metodę z tej prywatnej metody. Czy nie dlatego?
jingx
Zadajesz złe pytanie. Widoczność metod została wybrana, aby zawęzić lub poszerzyć możliwości wzajemnego oddziaływania obiektów. Jest to ważne, ponieważ programiści komunikujący się zamierzają, w jaki sposób ich kod powinien / musi / może być używany. Możesz zaimplementować wszystko w metodach statycznych lub w ogóle nie używać metod statycznych. Pytanie jest ważne, ponieważ musimy pomyśleć o konsekwencjach dostępu innych obiektów / klas do funkcjonalności, które w ogóle nie powinny być dostępne.
Florian Salihovic
2
@FlorianSalihovic Ale, jak dowiedziałem się z komentarzy ludzi, OP nie pytało o widoczność ani kiedy używać statycznego vs niestatycznego, zamiast tego pytało, dlaczego prywatne metody statyczne są nawet dozwolone na interfejsach, gdy prywatna statyczna pozornie wystarcza. Moja odpowiedź podała przypadek użycia, w którym działałaby tylko metoda niestatyczna.
jingx
3

Interfejsy służą do definiowania zachowania obiektu. Oznacza to, że wszystkie metody interfejsu są widoczne. Korzystając z metod domyślnych, możemy zapewnić standardowe implementacje zdefiniowanych metod, oferując ponowne użycie kodu ponad granicami klas.

W niektórych przypadkach wymagana jest funkcjonalność (być może tylko do ponownego użycia kodu w różnych metodach domyślnych ), ale nie należy jej ujawniać, ponieważ spowodowałoby to zanieczyszczenie przestrzeni nazw klasy / obiektu. Tutaj przydają się prywatne domyślne metody . Przykładami prywatnych metod domyślnych mogą być fabryki, walidacje lub domyślna obsługa stanu.

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
źródło