Trong Java, khi nào chúng ta nên sử dụng các phương thức cá nhân trong các giao diện?


9

Kể từ Java 9, các phương thức trong một giao diện có thể là riêng tư. Một phương thức riêng có thể là tĩnh hoặc một phương thức thể hiện. Vì các phương thức riêng tư chỉ có thể được sử dụng trong các phương thức của chính giao diện, nên việc sử dụng chúng bị giới hạn là các phương thức trợ giúp cho các phương thức khác của giao diện.

Cây S. Horstmann, Lõi Java Tập I - Nguyên tắc cơ bản

Tôi hiểu rằng chúng ta có thể đặt chức năng chung trong các phương thức riêng tư và không thể truy cập công khai. Nhưng chúng ta có thể có hai loại phương thức riêng tư ở đây:

  1. private
  2. private static

Sử dụng private staticphương pháp là điều dễ hiểu, nhưng khi nào chúng ta nên sử dụng privatephương pháp? Chúng tôi không xử lý các trường hợp ở đây vì đây là giao diện, vậy tại sao việc tạo privatephương thức được cho phép? Chúng ta không cần private staticphương pháp duy nhất ?


Một giao diện có thể bao gồm các phương thức mà các phương thức thể hiện khác gọi, nhưng không dành cho tiêu dùng công cộng.
Dave Newton

2
Hãy thử gọi privatephương thức cá thể của giao diện trong lớp thực hiện giao diện.
Abra

1
Một phương thức riêng tư như vậy có thể gọi các phương thức khác từ giao diện, vì vậy chúng không tương đương hoặc có thể thay thế bằng private staticcác phương thức.
Đánh dấu Rotteveel

phương pháp mặc định có lẽ
Maurice Perry

Câu trả lời:


2

OK, một nỗ lực khác thực sự trả lời các câu hỏi của OP. Khi bạn cần gọi một phương thức không tĩnh khác trên giao diện từ một phương thức riêng tư, phương thức riêng tư không thể là tĩnh. Ví dụ: sẽ có lỗi biên dịch nếu phương thức riêng tư bên dưới là tĩnh:

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

Tại sao điều đó có liên quan? Bạn cũng có thể thực hiện mọi phương thức là "mặc định công khai". Câu hỏi là về lý do tại sao / với ý định nào bạn sẽ chọn thực hiện x hoặc y trên z - không phải như thế nào.
Florian Salihovic

2
@FlorianSalihovic bạn sẽ chọn không tĩnh hơn tĩnh khi bạn cần gọi một phương thức khác từ phương thức riêng tư này. Đó không phải là lý do tại sao?
jingx

Bạn đang hỏi sai câu hỏi. Khả năng hiển thị của các phương thức được chọn để thu hẹp hoặc mở rộng các khả năng về cách các đối tượng tương tác với nhau. Điều quan trọng là các nhà phát triển truyền đạt ý định về cách mã của họ nên / phải / có thể được sử dụng. Bạn có thể thực hiện mọi thứ trong các phương thức tĩnh hoặc hoàn toàn không sử dụng phương thức tĩnh nào. Câu hỏi rất quan trọng vì chúng ta cần suy nghĩ về hậu quả của việc các đối tượng / lớp khác truy cập vào chức năng, điều này hoàn toàn không thể truy cập được.
Florian Salihovic

2
@FlorianSalihovic Nhưng như tôi đã học được từ ý kiến ​​của mọi người, OP đã không hỏi về khả năng hiển thị hoặc khi nào nên sử dụng tĩnh so với không tĩnh, thay vào đó họ hỏi tại sao các phương thức riêng tư không tĩnh thậm chí được phép trên các giao diện khi tĩnh riêng dường như đủ. Câu trả lời của tôi cung cấp một trường hợp sử dụng trong đó chỉ có một phương thức không tĩnh sẽ hoạt động.
jingx

3

Các giao diện được sử dụng để xác định hành vi của một đối tượng. Điều này có nghĩa là tất cả các phương thức của giao diện được phơi bày. Khi sử dụng các phương thức mặc định, chúng tôi có thể cung cấp các triển khai tiêu chuẩn của các phương thức được xác định, cung cấp việc sử dụng lại mã qua các ranh giới lớp.

Trong một số trường hợp, chức năng là bắt buộc (có lẽ chỉ để sử dụng lại mã trong các phương thức mặc định khác nhau ) nhưng không nên bị lộ vì nó sẽ gây ô nhiễm không gian tên của lớp / đối tượng. Đây là nơi các phương thức mặc định riêng tư có ích. Ví dụ các phương thức mặc định riêng tư có thể là các nhà máy, xác nhận hoặc xử lý trạng thái mặc định.

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);
    }
  }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.