In Java, quando dovremmo usare metodi di istanza privati ​​nelle interfacce?


9

A partire da Java 9, i metodi in un'interfaccia possono essere privati. Un metodo privato può essere statico o un metodo di istanza. Poiché i metodi privati ​​possono essere utilizzati solo nei metodi dell'interfaccia stessa, il loro uso è limitato ai metodi di supporto per gli altri metodi dell'interfaccia.

Cay S. Horstmann, Core Java Volume I - Fondamenti

Capisco che possiamo mettere la funzionalità comune nei metodi privati ​​e non renderla accessibile al pubblico. Ma qui possiamo avere due tipi di metodi privati:

  1. private
  2. private static

L'uso dei private staticmetodi è comprensibile, ma quando dovremmo usare i privatemetodi? Non abbiamo a che fare con istanze qui poiché si tratta di un'interfaccia, quindi perché privateè consentita la creazione di metodi? Non abbiamo bisogno solo di private staticmetodi?


Un'interfaccia può includere metodi chiamati da altri metodi di istanza, ma non destinati al consumo pubblico.
Dave Newton,

2
Prova a chiamare il privatemetodo di istanza dell'interfaccia nella classe che implementa l'interfaccia.
Abra,

1
Un tale metodo privato potrebbe chiamare altri metodi dall'interfaccia, quindi non sono equivalenti o sostituibili con private staticmetodi.
Mark Rotteveel,

metodi di default forse
Maurice Perry,

Risposte:


2

OK, un altro tentativo di rispondere effettivamente alle domande di OP. Quando è necessario chiamare un altro metodo non statico sull'interfaccia da un metodo privato, il metodo privato non può essere statico. Ad esempio, ci sarebbe un errore di compilazione se il metodo privato di seguito fosse statico:

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();
    }
}

Perché è rilevante? Puoi anche implementare ogni metodo come "default pubblico". La domanda è sul perché / con quale intenzione scegliere l'implementazione x o y su z - non come.
Florian Salihovic,

2
@FlorianSalihovic sceglieresti non statico su statico quando devi chiamare un altro metodo da questo metodo privato. Non è questo il motivo?
jingx,

Stai facendo la domanda sbagliata. La visibilità dei metodi viene scelta per restringere o ampliare le possibilità su come gli oggetti interagiscono tra loro. È importante poiché gli sviluppatori che comunicano intendono come il loro codice deve / deve / può essere utilizzato. Puoi implementare tutto in metodi statici o non utilizzare alcun metodo statico. La domanda è importante in quanto dobbiamo pensare alle conseguenze di avere altri oggetti / classi accesso alla funzionalità, che non dovrebbe essere affatto accessibile.
Florian Salihovic,

2
@FlorianSalihovic Ma, come ho appreso dai commenti della gente, OP non chiedeva visibilità o quando usare statico vs non statico, ma chiedevano perché i metodi privati ​​non statici fossero persino permessi sulle interfacce quando apparentemente il statico privato era sufficiente. La mia risposta ha fornito un caso d'uso in cui avrebbe funzionato solo un metodo non statico.
jingx,

3

Le interfacce vengono utilizzate per definire il comportamento di un oggetto. Ciò significa che tutti i metodi dell'interfaccia sono esposti. Quando si utilizzano metodi predefiniti, possiamo fornire implementazioni standard dei metodi definiti, offrendo il riutilizzo del codice oltre i limiti della classe.

In alcuni casi, è necessaria la funzionalità (forse solo per il riutilizzo del codice in diversi metodi predefiniti ) ma non deve essere esposta perché inquinerebbe gli spazi dei nomi di classe / oggetto. È qui che sono utili i metodi predefiniti privati . Esempi di metodi predefiniti privati ​​possono essere fabbriche, convalide o gestione dello stato predefinito.

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);
    }
  }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.