В Java, когда мы должны использовать частные методы экземпляра в интерфейсах?

9

Начиная с Java 9, методы в интерфейсе могут быть закрытыми. Закрытый метод может быть статическим или методом экземпляра. Поскольку частные методы могут использоваться только в методах самого интерфейса, их использование ограничено тем, чтобы быть вспомогательными методами для других методов интерфейса.

Кей С. Хорстманн, Core Java Том I - Основы

Я понял, что мы можем поместить общую функциональность в приватные методы и не делать ее доступной для общественности. Но у нас может быть два вида приватных методов:

  1. private
  2. private static

Использование private staticметодов понятно, но когда мы должны использовать privateметоды? Мы не имеем дело с экземплярами здесь, поскольку это интерфейс, так почему создание privateметодов разрешено? Разве нам не нужны только private staticметоды?

sg7610
источник
Интерфейс может включать методы, которые вызывают другие методы экземпляра, но не предназначены для общего пользования.
Дэйв Ньютон,
2
Попробуйте вызвать privateметод экземпляра интерфейса в классе, который реализует интерфейс.
Абра
1
Такой закрытый метод может вызывать другие методы из интерфейса, поэтому они не эквивалентны или не могут быть заменены private staticметодами.
Марк Роттвил
методы по умолчанию возможно
Морис Перри

Ответы:

2

ОК, еще одна попытка ответить на вопросы ОП. Когда вам нужно вызвать другой нестатический метод на интерфейсе из закрытого метода, закрытый метод не может быть статическим. Например, будет ошибка компиляции, если приведенный ниже закрытый метод был статическим:

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
источник
Почему это актуально? Вы также можете реализовать каждый метод как "public default". Вопрос в том, почему / с каким намерением вы бы выбрали реализацию x или y вместо z, а не как.
Флориан Салихович
2
@FlorianSalihovic вы бы выбрали не статический, а статический, когда вам нужно вызвать другой метод из этого частного метода. Разве не поэтому?
Jingx
Вы задаете неправильный вопрос. Видимость методов выбирается, чтобы сузить или расширить возможности взаимодействия объектов друг с другом. Это важно, так как разработчики сообщают о том, как их код должен / должен / может использоваться. Вы можете реализовать все в статических методах или вообще не использовать статических методов. Этот вопрос важен, поскольку нам нужно подумать о последствиях доступа других объектов / классов к функциональности, которая вообще не должна быть доступна.
Флориан Салихович
2
@FlorianSalihovic Но, как я узнал из комментариев людей, OP не спрашивал о видимости или о том, когда использовать статические, а не статические, вместо этого они спрашивали, почему нестатические приватные методы даже разрешены на интерфейсах, когда, казалось бы, достаточно частной статической. В моем ответе был случай, когда будет работать только нестатический метод.
Jingx
3

Интерфейсы используются для определения поведения объекта. Это означает, что все методы интерфейса доступны. При использовании методов по умолчанию мы можем предоставить стандартные реализации определенных методов, предлагая повторное использование кода через границы классов.

В некоторых случаях требуется функциональность (возможно, только для повторного использования кода в различных методах по умолчанию ), но ее не следует подвергать воздействию, поскольку она может загрязнить пространства имен класса / объекта. Здесь частные методы по умолчанию пригодятся. Примерами частных методов по умолчанию могут быть фабрики, проверки или обработка состояния по умолчанию.

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);
    }
  }
}
Флориан Салихович
источник