Em Java, quando devemos usar métodos de instância privada em interfaces?

9

No Java 9, os métodos em uma interface podem ser privados. Um método privado pode ser estático ou um método de instância. Como os métodos privados só podem ser usados ​​nos métodos da própria interface, seu uso é limitado a ser métodos auxiliares para os outros métodos da interface.

Cay S. Horstmann, Core Java Volume I - Fundamentos

Entendo que podemos colocar a funcionalidade comum nos métodos privados e não torná-la acessível ao público. Mas podemos ter dois tipos de métodos privados aqui:

  1. private
  2. private static

Usar private staticmétodos é compreensível, mas quando devemos usar privatemétodos? Não estamos lidando com instâncias aqui, pois essa é uma interface. Por que a criação de privatemétodos é permitida? Não precisamos apenas de private staticmétodos?

sg7610
fonte
Uma interface pode incluir métodos que outros métodos de instância chamam, mas não se destinam ao consumo público.
Dave Newton
2
Tente chamar o privatemétodo de instância da interface na classe que implementa a interface.
Abra
11
Um método privado pode chamar outros métodos da interface, portanto eles não são equivalentes ou substituíveis por private staticmétodos.
Mark Rotteveel
métodos padrão talvez
Maurice Perry

Respostas:

2

OK, outra tentativa de realmente responder às perguntas do OP. Quando você precisa chamar outro método não estático na interface a partir de um método privado, o método privado não pode ser estático. Por exemplo, haveria um erro de compilação se o método privado abaixo fosse estático:

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
fonte
Por que isso é relevante? Você também pode implementar todos os métodos como "padrão público". A questão é sobre o porquê / com qual intenção você escolheria a implementação x ou y em vez de z - não como.
Florian Salihovic
2
@FlorianSalihovic, você escolheria não estático em vez de estático quando precisar chamar outro método desse método particular. Não é por isso que?
jingx
Você está fazendo a pergunta errada. A visibilidade dos métodos é escolhida para restringir ou ampliar as possibilidades de como os objetos interagem entre si. É importante que os desenvolvedores que se comunicam pretendam como seu código deve / deve / pode ser usado. Você pode implementar tudo em métodos estáticos ou não usar nenhum método estático. A questão é importante, pois precisamos pensar nas consequências de ter outros objetos / classes acessando a funcionalidade, que não devem estar acessíveis.
Florian Salihovic
2
@FlorianSalihovic Mas, como aprendi com os comentários das pessoas, o OP não estava perguntando sobre visibilidade ou quando usar estático versus não estático; em vez disso, perguntava por que métodos privados não estáticos são permitidos nas interfaces quando a estática privada parece suficiente. Minha resposta forneceu um caso de uso em que apenas um método não estático funcionaria.
jingx
3

As interfaces são usadas para definir o comportamento de um objeto. Isso significa que todos os métodos da interface estão expostos. Ao usar métodos padrão, podemos fornecer implementações padrão dos métodos definidos, oferecendo reutilização de código além dos limites da classe.

Em alguns casos, a funcionalidade é necessária (talvez apenas para reutilização de código em diferentes métodos padrão ), mas não deve ser exposta, pois poluiria os namespaces da classe / objeto. É aqui que os métodos padrão privados são úteis. Exemplos de métodos padrão privados podem ser fábricas, validações ou manipulação de estado padrão.

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
fonte