Tại sao tôi không thể định nghĩa một phương thức tĩnh trong giao diện Java?


499

EDIT: Kể từ Java 8, các phương thức tĩnh hiện được phép trong các giao diện.

Đây là ví dụ:

public interface IXMLizable<T>
{
  static T newInstanceFromXML(Element e);
  Element toXMLElement();
}

Tất nhiên điều này sẽ không hoạt động. Nhưng tại sao không?

Một trong những vấn đề có thể xảy ra là, điều gì xảy ra khi bạn gọi:

IXMLizable.newInstanceFromXML(e);

Trong trường hợp này, tôi nghĩ rằng nó chỉ nên gọi một phương thức rỗng (tức là {}). Tất cả các lớp con sẽ bị buộc phải thực hiện phương thức tĩnh, vì vậy tất cả chúng đều ổn khi gọi phương thức tĩnh. Vậy tại sao điều này là không thể?

EDIT: Tôi đoán tôi đang tìm kiếm câu trả lời sâu sắc hơn "bởi vì đó là cách Java".

Có một lý do công nghệ cụ thể tại sao các phương thức tĩnh không thể được ghi đè? Đó là, tại sao các nhà thiết kế của Java quyết định tạo ra các phương thức cá thể quá mức nhưng không phải là phương thức tĩnh?

EDIT: Vấn đề với thiết kế của tôi là tôi đang cố gắng sử dụng các giao diện để thực thi quy ước mã hóa.

Đó là, mục tiêu của giao diện có hai mặt:

  1. Tôi muốn giao diện IXMLizable cho phép tôi chuyển đổi các lớp triển khai nó thành các phần tử XML (sử dụng đa hình, hoạt động tốt).

  2. Nếu ai đó muốn tạo một phiên bản mới của một lớp thực hiện giao diện IXMLizable, họ sẽ luôn biết rằng sẽ có một hàm tạo tĩnh mớiInstanceFromXML (Element e).

Có cách nào khác để đảm bảo điều này, ngoài việc chỉ đưa ra nhận xét trong giao diện không?


4
Bạn không cần phải lộn xộn các định nghĩa phương thức (và trường) với công khai trong các giao diện, btw.
Tom Hawtin - tackline

Hmm, dường như là một bản sao của stackoverflow.com/questions/21817/ . Chưa từng thấy điều đó trước đây.
Michael Myers

1
Bạn có thể cung cấp một số mã như thế nào bạn muốn sử dụng các phương thức giao diện tĩnh?
Pavel Feldman

43
Điều này sẽ có thể có trong Java 8: docs.oracle.com/javase/tutorial/java/IandI/ mẹo
dakshang

1
@dakshang Có, nhưng nó không làm những gì OP muốn.
dùng253751

Câu trả lời:


518

Java 8 cho phép các phương thức giao diện tĩnh

Với Java 8, các giao diện có thể có các phương thức tĩnh. Họ cũng có thể có các phương thức cá thể cụ thể, nhưng không phải là các trường mẫu.

Thực sự có hai câu hỏi ở đây:

  1. Tại sao, trong những ngày xưa tồi tệ, các giao diện không thể chứa các phương thức tĩnh?
  2. Tại sao các phương thức tĩnh không thể bị ghi đè?

Phương thức tĩnh trong giao diện

Không có lý do kỹ thuật mạnh mẽ tại sao các giao diện không thể có các phương thức tĩnh trong các phiên bản trước. Điều này được tóm tắt độc đáo bởi poster của một câu hỏi trùng lặp. Các phương thức giao diện tĩnh ban đầu được coi là một thay đổi ngôn ngữ nhỏ, và sau đó đã có một đề xuất chính thức để thêm chúng vào Java 7, nhưng sau đó đã bị loại bỏ do các biến chứng không lường trước được.

Cuối cùng, Java 8 đã giới thiệu các phương thức giao diện tĩnh, cũng như các phương thức cá thể có khả năng ghi đè với cách triển khai mặc định. Họ vẫn không thể có các trường ví dụ. Các tính năng này là một phần của hỗ trợ biểu thức lambda và bạn có thể đọc thêm về chúng trong Phần H của JSR 335.

Ghi đè các phương thức tĩnh

Câu trả lời cho câu hỏi thứ hai phức tạp hơn một chút.

Các phương thức tĩnh có thể phân giải được tại thời gian biên dịch. Công văn động có ý nghĩa đối với các phương thức ví dụ, trong đó trình biên dịch không thể xác định loại cụ thể của đối tượng và do đó, không thể giải quyết phương thức để gọi. Nhưng việc gọi một phương thức tĩnh đòi hỏi phải có một lớp và vì lớp đó được biết là tĩnh, thời gian biên dịch thời gian biên dịch động không cần thiết.

Một chút nền tảng về cách các phương thức hoạt động là cần thiết để hiểu những gì đang diễn ra ở đây. Tôi chắc rằng việc triển khai thực tế khá khác nhau, nhưng hãy để tôi giải thích khái niệm của tôi về việc gửi phương thức, mô hình nào quan sát chính xác hành vi.

Giả sử rằng mỗi lớp có một bảng băm ánh xạ chữ ký phương thức (tên và loại tham số) thành một đoạn mã thực tế để thực hiện phương thức. Khi máy ảo cố gắng gọi một phương thức trên một cá thể, nó truy vấn đối tượng cho lớp của nó và tìm kiếm chữ ký được yêu cầu trong bảng của lớp. Nếu một cơ thể phương thức được tìm thấy, nó được gọi. Mặt khác, lớp cha của lớp được lấy và việc tra cứu được lặp lại ở đó. Điều này tiến hành cho đến khi phương thức được tìm thấy, hoặc không có thêm các lớp cha mẹ mà kết quả là a NoSuchMethodError.

Nếu cả siêu lớp và một lớp con đều có một mục trong các bảng của chúng cho cùng một chữ ký phương thức, thì phiên bản của lớp con sẽ được bắt gặp trước và phiên bản của lớp siêu lớp không bao giờ được sử dụng, đây là "ghi đè".

Bây giờ, giả sử chúng ta bỏ qua thể hiện đối tượng và chỉ bắt đầu với một lớp con. Việc giải quyết có thể tiến hành như trên, cung cấp cho bạn một loại phương thức tĩnh "có thể ghi đè". Tuy nhiên, độ phân giải có thể xảy ra tại thời gian biên dịch, do trình biên dịch bắt đầu từ một lớp đã biết, thay vì đợi cho đến khi bộ thực thi truy vấn một đối tượng thuộc loại không xác định cho lớp của nó. Không có điểm nào trong việc "ghi đè" một phương thức tĩnh vì người ta luôn có thể chỉ định lớp có chứa phiên bản mong muốn.


Trình xây dựng "giao diện"

Đây là một ít tài liệu hơn để giải quyết các chỉnh sửa gần đây cho câu hỏi.

Có vẻ như bạn muốn bắt buộc một cách hiệu quả một phương thức giống như hàm tạo cho mỗi lần thực hiện IXMLizable. Hãy quên việc cố gắng thực thi điều này với một giao diện trong một phút và giả vờ rằng bạn có một số lớp đáp ứng yêu cầu này. Làm thế nào bạn sẽ sử dụng nó?

class Foo implements IXMLizable<Foo> {
  public static Foo newInstanceFromXML(Element e) { ... }
}

Foo obj = Foo.newInstanceFromXML(e);

Vì bạn phải đặt tên rõ ràng cho loại cụ thể Fookhi "xây dựng" đối tượng mới, trình biên dịch có thể xác minh rằng nó thực sự có phương thức xuất xưởng cần thiết. Và nếu không, vậy thì sao? Nếu tôi có thể thực hiện một IXMLizablecái thiếu "hàm tạo" và tôi tạo một cá thể và chuyển nó vào mã của bạn, thì đó một IXMLizablegiao diện cần thiết.

Xây dựng là một phần của việc thực hiện, không phải giao diện. Bất kỳ mã nào hoạt động thành công với giao diện đều không quan tâm đến hàm tạo. Bất kỳ mã nào quan tâm đến hàm tạo cần phải biết loại cụ thể và giao diện có thể bị bỏ qua.



12
Lý do cho số 1 là nhiều thừa kế? Vì chúng ta có thể kế thừa từ nhiều giao diện, nếu hai giao diện có cùng chữ ký phương thức tĩnh và sau đó một lớp triển khai cả hai và gọi phương thức đó, thì mọi thứ có thể trở nên phức tạp theo cách mà người tạo ngôn ngữ Java muốn tránh bằng cách không cho phép thừa kế nhiều lớp trong nơi đầu tiên Lập luận tương tự rõ ràng có thể được tạo ra cho giao diện không cho phép bất kỳ định nghĩa phương thức nào trong chúng.
shrini1000

1
@ shrini1000 - Không, các phương thức tĩnh được giải quyết tại thời điểm biên dịch. Sự mơ hồ có thể được xử lý giống như cách nó được xử lý với các hằng số: với lỗi trình biên dịch. Tuy nhiên, đề xuất theo Project Coin đã bị từ chối, với lý do một số khó khăn không lường trước được. Không chắc chúng là gì, nhưng tôi không nghĩ nó nằm dọc theo những dòng này.
erickson

1
@ tgm1024 Có, phần "Giao diện của nhà xây dựng" giải thích lý do tại sao không cố gắng gọi hành vi đa hình thông qua một loại được biết đến trong thời gian biên dịch. Làm thế nào bạn sẽ gọi RESET()vào một lớp nhất định? Bạn sẽ viết SomeClass.RESET(). Vì vậy, bạn không cần một giao diện để mô tả API đó; nó tĩnh. Các giao diện được sử dụng khi bạn không biết loại cụ thể tại thời điểm biên dịch. Đó không bao giờ là trường hợp với một phương pháp tĩnh.
erickson

2
"Xây dựng là một phần của việc triển khai, không phải giao diện. Bất kỳ mã nào hoạt động thành công với giao diện đều không quan tâm đến hàm tạo." - Điều đó rõ ràng không đúng. Trong các ngôn ngữ khác (ví dụ Swift), tôi có thể tạo các phiên bản mới Tmà không cần biết Ttĩnh, bởi vì tôi hứa trong một giao diện rằng một hàm tạo nhất định (hoặc phương thức tĩnh) sẽ tồn tại trong thời gian chạy. Thực tế là thế hệ không thể chỉ định trong Java không có nghĩa là nó không phải là một điều có ý nghĩa.
Raphael

48

Điều này đã được hỏi và trả lời, ở đây

Để nhân đôi câu trả lời của tôi:

Không bao giờ có một điểm để khai báo một phương thức tĩnh trong một giao diện. Chúng không thể được thực thi bằng lệnh gọi bình thường MyInterface.staticMethod (). Nếu bạn gọi cho họ bằng cách chỉ định lớp triển khai MyImcellencor.staticMethod () thì bạn phải biết lớp thực tế, do đó không liên quan đến việc giao diện có chứa nó hay không.

Quan trọng hơn, các phương thức tĩnh không bao giờ bị ghi đè và nếu bạn cố gắng thực hiện:

MyInterface var = new MyImplementingClass();
var.staticMethod();

các quy tắc cho tĩnh nói rằng phương thức được định nghĩa trong kiểu var được khai báo phải được thực thi. Vì đây là một giao diện, điều này là không thể.

Lý do bạn không thể thực thi "result = MyInterface.staticMethod ()" là vì nó sẽ phải thực thi phiên bản của phương thức được xác định trong MyInterface. Nhưng không thể có phiên bản được xác định trong MyInterface, vì đó là giao diện. Nó không có mã theo định nghĩa.

Mặc dù bạn có thể nói rằng số tiền này là "vì Java thực hiện theo cách đó", nhưng thực tế, quyết định là kết quả hợp lý của các quyết định thiết kế khác, cũng được đưa ra vì lý do rất chính đáng.


14
Nếu bạn sử dụng <T extends MyInterface> làm tham số loại chung, sẽ rất tốt để đảm bảo thông qua giao diện mà T có thể .doS Something ().
Chris Betti

4
Trong khi tôi hiểu các đối số, tôi đồng ý với @Chris_Betti (ngay cả đối với các loại không chung chung): thật tuyệt khi cấu trúc mã đảm bảo rằng một số lớp thực hiện API tĩnh cụ thể. Có lẽ có thể sử dụng một khái niệm khác ...
Juh_ 16/07/2015

@Juh_, ..... hoặc một từ khóa mới nếu hoàn toàn bắt buộc. Tôi tin rằng staticdù sao cũng là một thuật ngữ khó hiểu trong ngôn ngữ và đã bị kéo dài quá mức. Vì vậy, có nó bằng chính nó đã sơ sài. Xem ví dụ của tôi ở trên stackoverflow.com/questions/512877/ cáp {shrug}.

3
Điều này có vẻ không đúng: "Không bao giờ có điểm để khai báo một phương thức tĩnh trong một giao diện." Nếu tôi có một tập hợp các lớp mà không được khởi tạo, có thể cung cấp cho tôi một số thông tin nhưng tôi cần một giao diện chung để đưa thông tin cấp lớp tĩnh này vào (tức là một giao diện với phương thức tĩnh có thể ghi đè) thì đó là cách sử dụng hợp lệ . Hãy suy nghĩ về sự phản chiếu ++, nơi bạn có thể nắm bắt thông tin meta về các thuộc tính của lớp mà không bị hack xung quanh với các thuộc tính, sự phản chiếu, v.v.
Jason

1
"Không bao giờ có một điểm để khai báo một phương thức tĩnh trong một giao diện." Điều này không đúng: hãy tưởng tượng hệ thống của bạn có trình phân giải lớp mặc định. Nếu nó phát hiện ra rằng bạn triển khai phương thức ContainerInjectionInterce :: created (Container $ container), thì nó sẽ tạo đối tượng với hàm này chẳng hạn.
dùng3790897

37

Thông thường, điều này được thực hiện bằng cách sử dụng mẫu Factory

public interface IXMLizableFactory<T extends IXMLizable> {
  public T newInstanceFromXML(Element e);
}

public interface IXMLizable {
  public Element toXMLElement();
}

7
+1 một mô hình nhà máy nghe có vẻ như là giải pháp cho vấn đề. (mặc dù không phải câu hỏi)
pvgoddijn

Ai đó có thể cho tôi biết ý nghĩa của việc đặt <T mở rộng IXMLizable> ở đây không. Tôi mới biết về java. Nó làm gì?
Nuwan Harshakumara Piyarathna

1
@NuwanHarshakumaraPiyarathna T phải là một lớp mở rộng IXMLizable. Nhìn vào khái
quát

37

Với sự ra đời của Java 8 , giờ đây có thể viết các phương thức mặc địnhtĩnh trong giao diện. docs.oracle/staticMethod

Ví dụ:

public interface Arithmetic {

    public int add(int a, int b);

    public static int multiply(int a, int b) {
        return a * b;
    }
}
public class ArithmaticImplementation implements Arithmetic {

    @Override
    public int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        int result = Arithmetic.multiply(2, 3);
        System.out.println(result);
    }
}

Kết quả : 6

MIPO: Gọi một phương thức giao diện tĩnh không yêu cầu phải được thực hiện bởi bất kỳ lớp nào. Chắc chắn, điều này xảy ra bởi vì các quy tắc tương tự cho các phương thức tĩnh trong siêu lớp áp dụng cho các phương thức tĩnh trên các giao diện.


Đây là một ví dụ hoàn hảo cho câu hỏi này.

21

Bởi vì các phương thức tĩnh không thể được ghi đè trong các lớp con và do đó chúng không thể trừu tượng. Và tất cả các phương thức trong một giao diện là, thực tế , trừu tượng.


2
Bạn luôn có thể buộc mỗi loại thực hiện bất kỳ phương thức giao diện tĩnh nào. Máy đánh chữ, có ai không?
MichaelGG

16
Bước ra ngoài chính mình và trả lời câu hỏi: Tại sao một phương thức tĩnh không thể bị ghi đè? Nếu các phương thức tĩnh có thể bị ghi đè, nó sẽ trông như thế nào? Bạn có thể làm gì với họ? Câu trả lời này về cơ bản là "Bạn không thể bởi vì bạn không thể."
erickson

10

Tại sao tôi không thể định nghĩa một phương thức tĩnh trong giao diện Java?

Thực tế bạn có thể trong Java 8.

Theo tài liệu Java :

Một phương thức tĩnh là một phương thức được liên kết với lớp trong đó nó được định nghĩa chứ không phải với bất kỳ đối tượng nào. Mỗi thể hiện của lớp chia sẻ các phương thức tĩnh của nó

Trong Java 8 một giao diện có thể có các phương thức mặc địnhphương thức tĩnh . Điều này giúp chúng tôi dễ dàng tổ chức các phương thức trợ giúp trong các thư viện của chúng tôi. Chúng ta có thể giữ các phương thức tĩnh cụ thể cho một giao diện trong cùng một giao diện chứ không phải trong một lớp riêng biệt.

Ví dụ về phương thức mặc định:

list.sort(ordering);

thay vì

Collections.sort(list, ordering);

Ví dụ về phương thức tĩnh (từ chính doc ):

public interface TimeClient {
    // ...
    static public ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default public ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }    
}

6

Các giao diện có liên quan đến tính đa hình vốn gắn liền với các thể hiện đối tượng, không phải các lớp. Do đó, tĩnh không có ý nghĩa trong ngữ cảnh của giao diện.


Rõ ràng, logic súc tích. Vâng đặt.
Oke Uwechue

6

Đầu tiên, tất cả các quyết định ngôn ngữ là quyết định của những người tạo ra ngôn ngữ. Không có gì trong thế giới của công nghệ phần mềm hoặc định nghĩa ngôn ngữ hoặc trình biên dịch / viết trình thông dịch nói rằng một phương thức tĩnh không thể là một phần của giao diện. Tôi đã tạo ra một vài ngôn ngữ và trình biên dịch bằng văn bản cho chúng - tất cả chỉ là ngồi xuống và xác định ngữ nghĩa có ý nghĩa. Tôi tranh luận rằng ngữ nghĩa của một phương thức tĩnh trong một giao diện rất rõ ràng - ngay cả khi trình biên dịch phải trì hoãn độ phân giải của phương thức theo thời gian.

Thứ hai, chúng tôi sử dụng tất cả các phương thức tĩnh có nghĩa là có một lý do hợp lệ để có một mẫu giao diện bao gồm các phương thức tĩnh - tôi không thể nói cho bất kỳ ai trong số các bạn, nhưng tôi sử dụng các phương thức tĩnh một cách thường xuyên.

Câu trả lời đúng nhất có lẽ là không có nhu cầu nhận thức, tại thời điểm ngôn ngữ được xác định, cho các phương thức tĩnh trong giao diện. Java đã phát triển rất nhiều trong những năm qua và đây là một mục rõ ràng đã thu hút được một số sự quan tâm. Việc nó được xem xét cho Java 7 chỉ ra rằng nó đã tăng đến một mức độ quan tâm có thể dẫn đến thay đổi ngôn ngữ. Tôi, với một người, sẽ rất vui khi tôi không còn phải khởi tạo một đối tượng chỉ để tôi có thể gọi phương thức getter không tĩnh của mình để truy cập một biến tĩnh trong một thể hiện của lớp con ...


5

Các phương thức tĩnh không phải là các phương thức cá thể ảo, vì vậy tôi cho rằng các nhà thiết kế Java đã quyết định rằng họ không muốn chúng trong các giao diện.

Nhưng bạn có thể đặt các lớp chứa các phương thức tĩnh bên trong các giao diện. Bạn có thể thử nó!

public interface Test {
    static class Inner {
        public static Object get() {
            return 0;
        }
    }
}

5
  • "Có một lý do cụ thể mà các phương thức tĩnh không thể bị ghi đè".

Hãy để tôi từ lại câu hỏi đó cho bạn bằng cách điền vào các định nghĩa.

  • "Có một lý do cụ thể nào đó mà các phương thức được giải quyết trong thời gian biên dịch không thể được giải quyết trong thời gian chạy."

Hoặc, để đưa vào hoàn toàn hơn, Nếu tôi muốn gọi một phương thức mà không cần một cá thể, nhưng biết về lớp, làm thế nào tôi có thể giải quyết nó dựa trên trường hợp mà tôi không có.


3

Một số câu trả lời đã thảo luận về các vấn đề với khái niệm phương pháp tĩnh quá mức. Tuy nhiên, đôi khi bạn bắt gặp một mẫu mà dường như đó chỉ là những gì bạn muốn sử dụng.

Ví dụ, tôi làm việc với một lớp quan hệ đối tượng có các đối tượng giá trị, nhưng cũng có các lệnh để thao tác các đối tượng giá trị. Vì nhiều lý do khác nhau, mỗi lớp đối tượng giá trị phải xác định một số phương thức tĩnh cho phép khung tìm thấy thể hiện lệnh. Ví dụ: để tạo Người bạn sẽ làm:

cmd = createCmd(Person.getCreateCmdId());
Person p = cmd.execute();

và để tải một người bằng ID bạn sẽ làm

cmd = createCmd(Person.getGetCmdId());
cmd.set(ID, id);
Person p = cmd.execute();

Điều này khá thuận tiện, tuy nhiên nó có vấn đề của nó; đáng chú ý là sự tồn tại của các phương thức tĩnh không thể được thực thi trong giao diện. Một phương thức tĩnh có thể ghi đè trong giao diện sẽ chính xác là những gì chúng ta cần, nếu chỉ nó có thể hoạt động bằng cách nào đó.

Các EJB giải quyết vấn đề này bằng cách có giao diện Home; mỗi đối tượng biết cách tìm Trang chủ và Trang chủ chứa các phương thức "tĩnh". Bằng cách này, các phương thức "tĩnh" có thể được ghi đè khi cần và bạn không làm lộn xộn giao diện bình thường (được gọi là "Từ xa") với các phương thức không áp dụng cho một thể hiện của bean của bạn. Chỉ cần làm cho giao diện bình thường chỉ định phương thức "getHome ()". Trả về một thể hiện của đối tượng Home (có thể là một singleton, tôi cho rằng) và người gọi có thể thực hiện các hoạt động ảnh hưởng đến tất cả các đối tượng Person.


3

Bình luận EDIT: As of Java 8, static methods are now allowed in interfaces.

Đúng vậy, các phương thức tĩnh vì Java 8 được cho phép trong các giao diện, nhưng ví dụ của bạn vẫn không hoạt động. Bạn không thể chỉ định nghĩa một phương thức tĩnh: bạn phải thực hiện nó hoặc bạn sẽ gặp lỗi biên dịch.


2

Chà, không có khái quát, giao diện tĩnh là vô dụng vì tất cả các lệnh gọi phương thức tĩnh được giải quyết tại thời gian biên dịch. Vì vậy, không có sử dụng thực sự cho họ.

Với generic, họ đã sử dụng - có hoặc không có triển khai mặc định. Rõ ràng là sẽ cần phải ghi đè và như vậy. Tuy nhiên, tôi đoán là việc sử dụng như vậy không phải là OO (vì các câu trả lời khác chỉ ra một cách khó hiểu) và do đó không được coi là xứng đáng với nỗ lực mà họ cần để thực hiện một cách hữu ích.


1
Thuốc generic phải làm gì với điều này? Một phương thức tĩnh trên một giao diện sẽ vẫn không thể thực hiện được.
DJClayworth

Đầu tiên, đó là một quyết định thực hiện. Nhưng tôi đoán anh ta không muốn gọi các phương thức tĩnh trên một giao diện (anh ta chỉ có thể sử dụng một lớp). Nhưng thay vào đó, muốn có một cái gì đó giống như một kiểu chữ hoặc không chú ý đến các tham số kiểu. Trong thực tế, chỉnh sửa mới nhất của ông cho thấy điều này thậm chí còn rõ ràng hơn.
MichaelGG

2
Why can't I define a static method in a Java interface?

Tất cả các phương thức trong một giao diện đều trừu tượng rõ ràng và do đó bạn không thể định nghĩa chúng là tĩnh vì các phương thức tĩnh không thể trừu tượng.


1

Một giao diện không bao giờ có thể được hủy đăng ký tĩnh, ví dụ ISomething.member. Một giao diện luôn được hủy đăng ký thông qua một biến đề cập đến một thể hiện của một lớp con của giao diện. Do đó, một tham chiếu giao diện không bao giờ có thể biết nó thuộc lớp con nào mà không có một thể hiện của lớp con của nó.

Do đó, gần đúng nhất với một phương thức tĩnh trong một giao diện sẽ là một phương thức không tĩnh mà bỏ qua "cái này", tức là không truy cập bất kỳ thành viên không tĩnh nào của thể hiện. Ở mức độ trừu tượng thấp, mọi phương thức không tĩnh (sau khi tra cứu trong bất kỳ vtable nào) thực sự chỉ là một hàm với phạm vi lớp lấy "này" làm tham số chính thức ẩn. Xem đối tượng đơn lẻ của Scala và khả năng tương tác với Java là bằng chứng của khái niệm đó. Và do đó, mọi phương thức tĩnh là một hàm có phạm vi lớp không lấy tham số "này". Do đó, thông thường một phương thức tĩnh có thể được gọi là tĩnh, nhưng như đã nêu trước đây, một giao diện không có triển khai (là trừu tượng).

Do đó, để có được xấp xỉ gần nhất với một phương thức tĩnh trong một giao diện, là sử dụng một phương thức không tĩnh, sau đó không truy cập bất kỳ thành viên thể hiện không tĩnh nào. Sẽ không có hiệu suất có thể có lợi cho bất kỳ cách nào khác, bởi vì không có cách nào để liên kết tĩnh (tại thời gian biên dịch) a ISomething.member(). Lợi ích duy nhất tôi thấy của một phương thức tĩnh trong giao diện là nó sẽ không nhập (tức là bỏ qua) một "cái này" ẩn và do đó không cho phép truy cập vào bất kỳ thành viên thể hiện không tĩnh nào. Điều này sẽ tuyên bố ngầm định rằng hàm không truy cập "này", là bất biến và thậm chí không chỉ đọc đối với lớp chứa của nó. Nhưng một tuyên bố "tĩnh" trong một giao diện ISomethingcũng sẽ gây nhầm lẫn cho những người đã cố gắng truy cập nóISomething.member()mà sẽ gây ra một lỗi biên dịch. Tôi cho rằng nếu lỗi trình biên dịch đã được giải thích đầy đủ, sẽ tốt hơn là cố gắng giáo dục mọi người về việc sử dụng một phương thức không tĩnh để thực hiện những gì họ muốn (rõ ràng là phương pháp nhà máy), như chúng tôi đang làm ở đây (và đã được lặp lại trong 3 Q & A lần trên trang web này), vì vậy đây rõ ràng là một vấn đề không trực quan đối với nhiều người. Tôi đã phải suy nghĩ về nó trong một thời gian để có được sự hiểu biết chính xác.

Cách để có được một trường tĩnh có thể thay đổi trong một giao diện là sử dụng các phương thức getter và setter không tĩnh trong một giao diện, để truy cập vào trường tĩnh đó trong lớp con. Sidenote, các thống kê rõ ràng bất biến có thể được khai báo trong giao diện Java với static final.


0

Các giao diện chỉ cung cấp một danh sách những thứ mà một lớp sẽ cung cấp, chứ không phải là một triển khai thực tế của những thứ đó, đó là mục tĩnh của bạn.

Nếu bạn muốn thống kê, sử dụng một lớp trừu tượng và kế thừa nó, nếu không, loại bỏ tĩnh.

Mong rằng sẽ giúp!


2
Về lý thuyết, bạn có thể định nghĩa một giao diện để bao gồm một hành vi tĩnh, nghĩa là "việc triển khai giao diện này sẽ có một phương thức tĩnh foo () với chữ ký này" và để việc triển khai đến lớp cụ thể. Tôi đã gặp tình huống mà hành vi này sẽ hữu ích.
Cướp

0

Bạn không thể định nghĩa các phương thức tĩnh trong một giao diện vì các phương thức tĩnh thuộc về một lớp không thuộc một thể hiện của lớp và các giao diện không phải là các Lớp. Đọc thêm tại đây.

Tuy nhiên, nếu bạn muốn bạn có thể làm điều này:

public class A {
  public static void methodX() {
  }
}

public class B extends A {
  public static void methodX() {
  }
}

Trong trường hợp này, cái bạn có là hai lớp với 2 phương thức tĩnh riêng biệt được gọi là methodX ().


0

Giả sử bạn có thể làm điều đó; xem xét ví dụ này:

interface Iface {
  public static void thisIsTheMethod();
}

class A implements Iface {

  public static void thisIsTheMethod(){
    system.out.print("I'm class A");
  }

}

class B extends Class A {

  public static void thisIsTheMethod(){
    System.out.print("I'm class B");
  } 
}

SomeClass {

  void doStuff(Iface face) {
    IFace.thisIsTheMethod();
    // now what would/could/should happen here.
  }

}

1
Nó sẽ in "Tôi là lớp A". Tuy nhiên, nếu bạn gõ A.thisIsTheMethod()nó sẽ in "Tôi là lớp B".
cdmckay

nhưng oyr gọi các phương thức trên giao diện, làm thế nào bạn (hoặc trình biên dịch) biết phương thức nào sẽ được gọi? (
pvgoddijn

xin lỗi, tôi muốn nói: Tuy nhiên, nếu bạn gõ B.thisIsTheMethod()nó sẽ in "Tôi là lớp B".
cdmckay

tôi đã nói IFace.thisIsTHeMethod về mục đích vì vấn đề nằm ở đó. sẽ không thể gọi nó trên giao diện mà không có hành vi không xác định (mặc dù nó đã được khai báo trên đó)
pvgoddijn

0

Một cái gì đó có thể được thực hiện là giao diện tĩnh (thay vì phương thức tĩnh trong giao diện). Tất cả các lớp thực hiện một giao diện tĩnh đã cho sẽ thực hiện các phương thức tĩnh tương ứng. Bạn có thể nhận SI giao diện tĩnh từ bất kỳ clazz Class nào bằng cách sử dụng

SI si = clazz.getStatic(SI.class); // null if clazz doesn't implement SI
// alternatively if the class is known at compile time
SI si = Someclass.static.SI; // either compiler errror or not null

sau đó bạn có thể gọi si.method(params). Điều này sẽ hữu ích (ví dụ đối với mẫu thiết kế nhà máy) bởi vì bạn có thể nhận (hoặc kiểm tra việc thực hiện) triển khai phương thức tĩnh SI từ một lớp không xác định thời gian biên dịch! Một công văn động là cần thiết và bạn có thể ghi đè các phương thức tĩnh (nếu không phải là cuối cùng) của một lớp bằng cách mở rộng nó (khi được gọi thông qua giao diện tĩnh). Rõ ràng, các phương thức này chỉ có thể truy cập các biến tĩnh của lớp của chúng.


0

Trong khi tôi nhận ra rằng Java 8 giải quyết vấn đề này, tôi nghĩ rằng tôi đã đồng ý với một kịch bản mà tôi hiện đang làm việc (bị khóa bằng cách sử dụng Java 7) trong đó việc có thể chỉ định các phương thức tĩnh trong giao diện sẽ hữu ích.

Tôi có một số định nghĩa enum trong đó tôi đã xác định các trường "id" và "displayName" cùng với các phương thức của trình trợ giúp đánh giá các giá trị vì nhiều lý do. Việc thực hiện một giao diện cho phép tôi đảm bảo rằng các phương thức getter được đặt đúng chỗ nhưng không phải là các phương thức trợ giúp tĩnh. Là một enum, thực sự không có cách nào rõ ràng để giảm tải các phương thức của trình trợ giúp vào một lớp trừu tượng được kế thừa hoặc một cái gì đó tương tự để các phương thức phải được định nghĩa trong chính enum. Ngoài ra, vì nó là một enum, bạn sẽ không thể thực sự vượt qua nó như một đối tượng được điều khiển và coi nó như kiểu giao diện, nhưng có thể yêu cầu sự tồn tại của các phương thức trợ giúp tĩnh thông qua giao diện là điều tôi thích nó đang được hỗ trợ trong Java 8.

Đây là mã minh họa quan điểm của tôi.

Định nghĩa giao diện:

public interface IGenericEnum <T extends Enum<T>> {
    String getId();
    String getDisplayName();
    //If I was using Java 8 static helper methods would go here
}

Ví dụ về một định nghĩa enum:

public enum ExecutionModeType implements IGenericEnum<ExecutionModeType> {
    STANDARD ("Standard", "Standard Mode"),
    DEBUG ("Debug", "Debug Mode");

    String id;
    String displayName;

    //Getter methods
    public String getId() {
        return id;
    }

    public String getDisplayName() {
        return displayName;
    }

    //Constructor
    private ExecutionModeType(String id, String displayName) {
        this.id = id;
        this.displayName = displayName;
    }

    //Helper methods - not enforced by Interface
    public static boolean isValidId(String id) {
        return GenericEnumUtility.isValidId(ExecutionModeType.class, id);
    }

    public static String printIdOptions(String delimiter){
        return GenericEnumUtility.printIdOptions(ExecutionModeType.class, delimiter);
    }

    public static String[] getIdArray(){
        return GenericEnumUtility.getIdArray(ExecutionModeType.class);
    }

    public static ExecutionModeType getById(String id) throws NoSuchObjectException {
        return GenericEnumUtility.getById(ExecutionModeType.class, id);
    }
}

Định nghĩa tiện ích chung enum:

public class GenericEnumUtility {
    public static <T extends Enum<T> & IGenericEnum<T>> boolean isValidId(Class<T> enumType, String id) {       
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(enumOption.getId().equals(id)) {
                return true;
            }
        }

        return false;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String printIdOptions(Class<T> enumType, String delimiter){
        String ret = "";
        delimiter = delimiter == null ? " " : delimiter;

        int i = 0;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(i == 0) {
                ret = enumOption.getId();
            } else {
                ret += delimiter + enumOption.getId();
            }           
            i++;
        }

        return ret;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String[] getIdArray(Class<T> enumType){
        List<String> idValues = new ArrayList<String>();

        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            idValues.add(enumOption.getId());
        }

        return idValues.toArray(new String[idValues.size()]);
    }

    @SuppressWarnings("unchecked")
    public static <T extends Enum<T> & IGenericEnum<T>> T getById(Class<T> enumType, String id) throws NoSuchObjectException {
        id = id == null ? "" : id;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(id.equals(enumOption.getId())) {
                return (T)enumOption;
            }
        }

        throw new NoSuchObjectException(String.format("ERROR: \"%s\" is not a valid ID. Valid IDs are: %s.", id, printIdOptions(enumType, " , ")));
    }
}

0

Giả sử các phương thức tĩnh được cho phép trong các giao diện: * Chúng sẽ buộc tất cả các lớp thực hiện khai báo phương thức đó. * Các giao diện thường sẽ được sử dụng thông qua các đối tượng, vì vậy các phương thức hiệu quả duy nhất trên đó là các phương thức không tĩnh. * Bất kỳ lớp nào biết một giao diện cụ thể đều có thể gọi các phương thức tĩnh của nó. Do đó phương thức tĩnh của lớp thực hiện sẽ được gọi bên dưới, nhưng lớp invoker không biết cái nào. Làm thế nào để biết nó? Nó không có khả năng đoán ngay!

Các giao diện được cho là được sử dụng khi làm việc với các đối tượng. Bằng cách này, một đối tượng được khởi tạo từ một lớp cụ thể, vì vậy vấn đề cuối cùng này được giải quyết. Lớp gọi không cần biết lớp cụ thể nào vì việc khởi tạo có thể được thực hiện bởi lớp thứ ba. Vì vậy, lớp gọi chỉ biết giao diện.

Nếu chúng ta muốn điều này được mở rộng thành các phương thức tĩnh, chúng ta sẽ có khả năng đặc biệt hóa một lớp thực hiện trước đó, sau đó chuyển một tham chiếu đến lớp gọi. Điều này có thể sử dụng lớp thông qua các phương thức tĩnh trong giao diện. Nhưng sự khác biệt giữa tài liệu tham khảo này và một đối tượng là gì? Chúng ta chỉ cần một đối tượng đại diện cho lớp. Bây giờ, đối tượng đại diện cho lớp cũ và có thể thực hiện một giao diện mới bao gồm các phương thức tĩnh cũ - những phương thức này hiện không tĩnh.

Metaclass phục vụ cho mục đích này. Bạn có thể thử lớp Class của Java. Nhưng vấn đề là Java không đủ linh hoạt cho việc này. Bạn không thể khai báo một phương thức trong đối tượng lớp của một giao diện.

Đây là một vấn đề meta - khi bạn cần làm ass

..blah blah

dù sao bạn cũng có một cách giải quyết dễ dàng - làm cho phương thức không tĩnh với cùng logic. Nhưng sau đó bạn sẽ phải tạo một đối tượng để gọi phương thức.


0

Để giải quyết điều này: error: phần thân phương thức bị thiếu hoặc khai báo static void void main (String [] args);

interface I
{
    int x=20;
    void getValue();
    static void main(String[] args){};//Put curly braces 
}
class InterDemo implements I
{
    public void getValue()
    {
    System.out.println(x);
    }
    public static void main(String[] args)
    {
    InterDemo i=new InterDemo();
    i.getValue();   
    }

}

đầu ra: 20

Bây giờ chúng ta có thể sử dụng phương thức tĩnh trong giao diện


1
Điều đó là vô dụng, mặc dù. Việc xác định một phương thức tĩnh trên một giao diện không thực thi các định nghĩa tiếp theo trong các lớp thực hiện giao diện đó. Nếu bạn chỉ loại bỏ phương thức tĩnh hoàn toàn khỏi giao diện I, mã của bạn sẽ biên dịch và chạy bằng mọi cách mà không gặp vấn đề gì. Nói cách khác, bạn KHÔNG ghi đè phương thức chính của giao diện I trong lớp InterDemo, chỉ tạo một phương thức mới có cùng chữ ký.
Fran Marzoa

-2

Tôi nghĩ java không có các phương thức giao diện tĩnh vì bạn không cần chúng. Bạn có thể nghĩ rằng bạn làm, nhưng ... Bạn sẽ sử dụng chúng như thế nào? Nếu bạn muốn gọi họ như

MyImplClass.myMethod()

sau đó bạn không cần phải khai báo nó trong giao diện. Nếu bạn muốn gọi họ như

myInstance.myMethod()

sau đó nó không nên tĩnh. Nếu bạn thực sự sẽ sử dụng cách đầu tiên, nhưng chỉ muốn thực thi mỗi triển khai để có phương thức tĩnh như vậy, thì đó thực sự là một quy ước mã hóa, không phải là một hợp đồng giữa thực hiện giao diện và mã gọi.

Các giao diện cho phép bạn xác định hợp đồng giữa các thể hiện của lớp thực hiện giao diện và mã gọi. Và java giúp bạn chắc chắn rằng hợp đồng này không bị vi phạm, vì vậy bạn có thể dựa vào nó và đừng lo lắng lớp nào thực hiện hợp đồng này, chỉ cần "ai đó đã ký hợp đồng" là đủ. Trong trường hợp giao diện tĩnh mã của bạn

MyImplClass.myMethod()

không dựa vào thực tế là mỗi triển khai giao diện có phương pháp này, vì vậy bạn không cần java để giúp bạn chắc chắn với nó.


-5

Nhu cầu của phương thức tĩnh trong giao diện là gì, về cơ bản các phương thức tĩnh được sử dụng khi bạn không phải tạo một thể hiện của toàn bộ đối tượng ý tưởng của giao diện là đưa vào các khái niệm OOP với việc giới thiệu phương thức tĩnh mà bạn chuyển hướng từ khái niệm.

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.