En Java, ¿cuándo debemos usar métodos de instancia privada en las interfaces?

9

A partir de Java 9, los métodos en una interfaz pueden ser privados. Un método privado puede ser estático o un método de instancia. Dado que los métodos privados solo pueden usarse en los métodos de la interfaz en sí, su uso se limita a ser métodos auxiliares para los otros métodos de la interfaz.

Cay S. Horstmann, Core Java Volumen I - Fundamentos

Entiendo que podemos poner la funcionalidad común en los métodos privados y no hacerla accesible al público. Pero podemos tener dos tipos de métodos privados aquí:

  1. private
  2. private static

El uso de private staticmétodos es comprensible, pero ¿cuándo debemos usarlos private? No estamos tratando con instancias aquí ya que esta es una interfaz, entonces, ¿por qué privateestá permitido crear métodos? ¿No necesitamos solo private staticmétodos?

sg7610
fuente
Una interfaz puede incluir métodos que otros métodos de instancia llaman, pero no están destinados al consumo público.
Dave Newton el
2
Intente llamar al privatemétodo de instancia de la interfaz en la clase que implementa la interfaz.
Abra
1
Tal método privado podría llamar a otros métodos desde la interfaz, por lo que no son equivalentes o reemplazables por private staticmétodos.
Mark Rotteveel
métodos predeterminados quizás
Maurice Perry

Respuestas:

2

OK, otro intento de responder las preguntas de OP. Cuando necesita llamar a otro método no estático en la interfaz desde un método privado, el método privado no puede ser estático. Por ejemplo, habría un error de compilación si el siguiente método privado fuera 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
fuente
¿Por qué es eso relevante? También puede implementar todos los métodos como "default público". La pregunta es por qué / con qué intención elegiría la implementación x o y sobre z, no cómo.
Florian Salihovic el
2
@FlorianSalihovic elegiría no estático sobre estático cuando necesite llamar a otro método desde este método privado. ¿No es ese el por qué?
jingx
Estás haciendo la pregunta equivocada. La visibilidad de los métodos se elige para reducir o ampliar las posibilidades de cómo los objetos interactúan entre sí. Es importante ya que los desarrolladores que se comunican tienen la intención de cómo debe / debe / puede usarse su código. Puede implementar todo en métodos estáticos o no utilizar ningún método estático. La pregunta es importante ya que debemos pensar en las consecuencias de tener otros objetos / clases con acceso a la funcionalidad, que no debería ser accesible en absoluto.
Florian Salihovic
2
@FlorianSalihovic Pero como aprendí de los comentarios de la gente, OP no preguntaba sobre la visibilidad o cuándo usar estática frente a no estática, sino que preguntaba por qué los métodos privados no estáticos están permitidos en las interfaces cuando la estática privada parece ser suficiente. Mi respuesta proporcionó un caso de uso donde solo funcionaría un método no estático.
jingx
3

Las interfaces se utilizan para definir el comportamiento de un objeto. Esto significa todo los métodos de la interfaz están expuestos. Cuando usamos métodos predeterminados, podemos proporcionar implementaciones estándar de los métodos definidos, ofreciendo reutilización de código a través de los límites de la clase.

En algunos casos, se requiere funcionalidad (quizás solo para la reutilización de código en diferentes métodos predeterminados ) pero no se debe exponer porque contaminaría los espacios de nombres de clase / objeto. Aquí es donde los métodos privados por defecto son útiles. Ejemplos de métodos privados predeterminados pueden ser fábricas, validaciones o manejo de estado predeterminado.

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
fuente