Tại sao tôi không thể khai báo các phương thức tĩnh trong một giao diện?


150

Chủ đề nói lên phần lớn của nó - lý do cho thực tế là các phương thức tĩnh không thể được khai báo trong một giao diện là gì?

public interface ITest {
    public static String test();
}

Đoạn mã trên cung cấp cho tôi lỗi sau (ít nhất là trong Eclipse): "Công cụ sửa đổi bất hợp pháp cho phương thức giao diện ITest.test (); chỉ cho phép công khai & trừu tượng".


2
Xin vui lòng không chấp nhận câu trả lời của Espo, vì nó là thiếu sót. Một giao diện có một tệp lớp có thể chứa việc triển khai một phương thức tĩnh (nếu trình thiết kế Java sẽ cho phép điều này), do đó không có vấn đề gì trong việc giải quyết việc thực hiện phương thức tĩnh. Nó hoạt động chính xác như với các lớp tĩnh khác.
Mnementh

Tôi đồng ý với câu trả lời được đưa ra bởi "erickson" stackoverflow.com/questions/512877/iêu
Maverick

9
Điều này sẽ có sẵn trong Java 8 btw.
m0skit0

1
@Vadorequest GIYF nhưng dù sao, hãy kiểm tra tại đây
m0skit0

2
Liên kết từ tài liệu chính thức: Hướng dẫn Java SE & Đặc tả ngôn ngữ Java 9.2
LoganMzz

Câu trả lời:


85

Có một vài vấn đề tại đây. Đầu tiên là vấn đề khai báo một phương thức tĩnh mà không xác định nó. Đây là sự khác biệt giữa

public interface Foo {
  public static int bar();
}

public interface Foo {
  public static int bar() {
    ...
  }
}

Đầu tiên là không thể vì những lý do mà Espo đề cập: bạn không biết lớp triển khai nào là định nghĩa chính xác.

Java có thể cho phép cái sau; và trên thực tế, bắt đầu từ Java 8, nó có!


2
Vâng - đó là ý tưởng phi kỹ thuật. Lý do tôi muốn nó là. rằng người ta có thể có một phương thức "triển khai" tĩnh trong một giao diện chỉ tham chiếu các phương thức "giao diện" khác trong giao diện có thể dễ dàng sử dụng lại bằng cách thực hiện các lớp. Nhưng người ta có thể khai báo một lớp tĩnh trong một giao diện để người ta có thể có những thứ như vậy nằm trong đó như MyInterface.Impl.doIt (MyInterface i, Object [] args) {...}
peterk

9
Kể từ Java 8, bạn có thể định nghĩa staticcác phương thức trong một interface. Các phương pháp phải được public.
Olivier Grégoire

4
@ OlivierGrégoire ... và chúng không được kế thừa, đó là chìa khóa.
William F. Jameson

1
Câu trả lời hay, mặc dù "tương đương" ROFLMAO xD tôi sẽ đặt nó giống như "hơi giống".
Timo

44

Lý do tại sao bạn không thể có một phương thức tĩnh trong một giao diện nằm ở cách Java giải quyết các tham chiếu tĩnh. Java sẽ không bận tâm tìm kiếm một thể hiện của một lớp khi cố gắng thực hiện một phương thức tĩnh. Điều này là do các phương thức tĩnh không phụ thuộc vào thể hiện và do đó có thể được thực thi trực tiếp từ tệp lớp. Do tất cả các phương thức trong một giao diện là trừu tượng, VM sẽ phải tìm kiếm một triển khai cụ thể của giao diện để tìm mã đằng sau phương thức tĩnh để có thể thực thi được. Điều này sau đó mâu thuẫn với cách thức giải quyết phương thức tĩnh và sẽ đưa ra sự không nhất quán vào ngôn ngữ.


3
Giải thích này không giải thích vấn đề. Mọi giao diện đều có tệp lớp riêng, nó có thể chứa phương thức tĩnh. Vì vậy, không cần tìm kiếm cho một thực hiện cụ thể sẽ là cần thiết.
Mnementh

Không phải mọi loại giao diện trong Java đều nằm trong tệp riêng của nó, cũng không phải theo JLS. Ngoài ra, JLS không quy định rằng các lớp phải luôn được lưu trữ cùng với một hệ thống tệp, hoàn toàn ngược lại.
Vlad Gudim

4
@Totophil: Các giao diện không được ở trong một tệp java, nhưng nó sẽ có một tệp lớp riêng sau khi biên dịch. Đó là những gì tôi đã viết.
Mnementh

18

Tôi sẽ trả lời câu hỏi của bạn bằng một ví dụ. Giả sử chúng ta đã có một lớp Math với phương thức tĩnh thêm. Bạn sẽ gọi phương thức này như vậy:

Math.add(2, 3);

Nếu Math là một giao diện thay vì một lớp, nó không thể có bất kỳ hàm xác định nào. Như vậy, nói điều gì đó như Math.add (2, 3) không có ý nghĩa gì.


11

Lý do nằm ở nguyên tắc thiết kế, rằng java không cho phép nhiều kế thừa. Vấn đề với nhiều kế thừa có thể được minh họa bằng ví dụ sau:

public class A {
   public method x() {...}
}
public class B {
   public method x() {...}
}
public class C extends A, B { ... }

Bây giờ điều gì xảy ra nếu bạn gọi Cx ()? Sẽ được Ax () hoặc Bx () thực thi? Mỗi ngôn ngữ có nhiều kế thừa phải giải quyết vấn đề này.

Các giao diện cho phép trong Java một số loại thừa kế bị hạn chế. Để tránh vấn đề trên, họ không được phép có phương pháp. Nếu chúng ta xem xét cùng một vấn đề với các giao diện và phương thức tĩnh:

public interface A {
   public static method x() {...}
}
public interface B {
   public static method x() {...}
}
public class C implements A, B { ... }

Vấn đề tương tự ở đây, điều gì xảy ra nếu bạn gọi Cx ()?


Bất kỳ lý do cho các downvote? Một nhận xét giải thích sẽ là tốt đẹp.
Mnementh

tôi không phải là downvoter, nhưng điều này không hợp lệ cho các phương thức không tĩnh?
nawfal

OK, đây là hai khả năng khác nhau. Một phương pháp có thể được thực hiện hoặc chỉ được khai báo. Tôi hiểu rằng phương pháp tĩnh phải được thực hiện. Trong ý nghĩa đó, tôi gặp phải vấn đề được trình bày trong câu trả lời của tôi. Nếu bạn không làm điều đó, bạn sẽ gặp phải vấn đề mà Espo đã mô tả - và tôi không hiểu vì tôi cho rằng phương thức tĩnh sẽ được thực hiện. Bạn cũng không thể khai báo một phương thức trừu tượng tĩnh vì lý do này, hãy thử nó, trình biên dịch sẽ phàn nàn.
Mnementh

Ok, quên phần thực hiện. Câu hỏi là tại sao chúng ta không thể tuyên bố. vâng, trình biên dịch sẽ phàn nàn và tại sao đó là câu hỏi. mà tôi không nghĩ rằng bạn đã trả lời.
nawfal

1
Làm thế nào tình huống có tồi tệ hơn việc có giao diện Achứa int x(int z);và giao diện Bchứa string x(int x);? Ý nghĩa của x(3)giao diện C là gì?
supercat

7

Phương thức tĩnh không phải là phương thức thể hiện. Không có bối cảnh ví dụ, do đó để thực hiện nó từ giao diện có ý nghĩa rất nhỏ.


5

Bây giờ Java8 cho phép chúng ta định nghĩa các Phương thức tĩnh trong Giao diện.

interface X {
    static void foo() {
       System.out.println("foo");
    }
}

class Y implements X {
    //...
}

public class Z {
   public static void main(String[] args) {
      X.foo();
      // Y.foo(); // won't compile because foo() is a Static Method of X and not Y
   }
}

Lưu ý: Theo mặc định, các phương thức trong Giao diện vẫn là công khai trừu tượng nếu chúng ta không sử dụng rõ ràng các từ khóa mặc định / tĩnh để biến chúng thành các phương thức mặc định và phương thức tĩnh.


4

Có một câu trả lời rất hay và súc tích cho câu hỏi của bạn ở đây . (Nó đánh tôi như một cách đơn giản để giải thích nó mà tôi muốn liên kết nó từ đây.)


Đây không phải là một câu trả lời cho câu hỏi, tốt nhất nó nên là một bình luận.
CubeJ Racer

3

Có vẻ như phương thức tĩnh trong giao diện có thể được hỗ trợ trong Java 8 , tốt, giải pháp của tôi chỉ là định nghĩa chúng trong lớp bên trong.

interface Foo {
    // ...
    class fn {
        public static void func1(...) {
            // ...
        }
    }
}

Kỹ thuật tương tự cũng có thể được sử dụng trong các chú thích:

public @interface Foo {
    String value();

    class fn {
        public static String getValue(Object obj) {
            Foo foo = obj.getClass().getAnnotation(Foo.class);
            return foo == null ? null : foo.value();
        }
    }
}

Lớp bên trong phải luôn được truy cập dưới dạng Interface.fn...thay vì Class.fn..., sau đó, bạn có thể thoát khỏi vấn đề mơ hồ.


2

Một giao diện được sử dụng cho đa hình, áp dụng cho các Đối tượng, không phải các loại. Do đó (như đã lưu ý) không có ý nghĩa gì khi có một thành viên giao diện tĩnh.


Trong một số bối cảnh phản chiếu dường như có ý nghĩa mặc dù
Cruncher

1

Java 8 Đã thay đổi thế giới bạn có thể có các phương thức tĩnh trong giao diện nhưng nó buộc bạn phải cung cấp triển khai cho điều đó.

public interface StaticMethodInterface {
public static int testStaticMethod() {
    return 0;
}

/**
 * Illegal combination of modifiers for the interface method
 * testStaticMethod; only one of abstract, default, or static permitted
 * 
 * @param i
 * @return
 */
// public static abstract int testStaticMethod(float i);

default int testNonStaticMethod() {
    return 1;
}

/**
 * Without implementation.
 * 
 * @param i
 * @return
 */
int testNonStaticMethod(float i);

}


0

Sự kết hợp bất hợp pháp của các sửa đổi: tĩnh và trừu tượng

Nếu một thành viên của một lớp được khai báo là tĩnh, nó có thể được sử dụng với tên lớp được giới hạn trong lớp đó mà không tạo đối tượng.

Nếu một thành viên của một lớp được khai báo là trừu tượng, bạn cần khai báo lớp là trừu tượng và bạn cần cung cấp việc thực hiện thành viên trừu tượng trong lớp kế thừa của nó (Lớp con).

Bạn cần cung cấp một triển khai cho thành viên trừu tượng của một lớp trong lớp con nơi bạn sẽ thay đổi hành vi của phương thức tĩnh, cũng được khai báo là trừu tượng, bị giới hạn trong lớp cơ sở, điều này không đúng


Làm thế nào để trả lời câu hỏi này? OP hỏi về giao diện trong khi bạn viết về lớp.
May mắn

0

Vì các phương thức tĩnh không thể được kế thừa. Vì vậy, không sử dụng đặt nó trong giao diện. Giao diện về cơ bản là một hợp đồng mà tất cả các thuê bao của nó phải tuân theo. Đặt một phương thức tĩnh trong giao diện sẽ buộc các thuê bao thực hiện nó. mà bây giờ trở nên mâu thuẫn với thực tế là các phương thức tĩnh không thể được kế thừa.


phương thức tĩnh luôn được kế thừa nhưng chúng không thể bị ghi đè.
Akki

0

Với Java 8 , các giao diện giờ đây có thể có các phương thức tĩnh.

Ví dụ, Trình so sánh có một phương thức staticOrder () tĩnh.

Yêu cầu rằng các giao diện không thể có triển khai cũng đã được nới lỏng. Các giao diện bây giờ có thể khai báo các triển khai phương thức "mặc định", giống như các triển khai bình thường với một ngoại lệ: nếu bạn kế thừa cả triển khai mặc định từ giao diện và triển khai bình thường từ siêu lớp, việc triển khai của siêu lớp sẽ luôn được ưu tiên.


CHÚA ƠI! Chúng tôi có một lập trình viên nghiêm túc (và tôi, người bình luận) trả lời (và tôi, bình luận) câu hỏi 10 năm tuổi.
Mohamed Anees A

Tôi đã không thông báo ngày :)
Ishara

Haha! Không vấn đề gì!
Mohamed Anees A

-2

Có lẽ một ví dụ mã sẽ giúp ích, tôi sẽ sử dụng C #, nhưng bạn sẽ có thể làm theo.

Hãy giả vờ rằng chúng ta có một giao diện gọi là IPayable

public interface IPayable
{
    public Pay(double amount);
}

Bây giờ, chúng ta có hai lớp cụ thể thực hiện giao diện này:

public class BusinessAccount : IPayable
{
    public void Pay(double amount)
    {
        //Logic
    }
}

public class CustomerAccount : IPayable
{
    public void Pay(double amount)
    {
        //Logic
    }
}

Bây giờ, hãy giả vờ rằng chúng tôi có một bộ sưu tập các tài khoản khác nhau, để làm điều này, chúng tôi sẽ sử dụng một danh sách chung về loại IPayable

List<IPayable> accountsToPay = new List<IPayable>();
accountsToPay.add(new CustomerAccount());
accountsToPay.add(new BusinessAccount());

Bây giờ, chúng tôi muốn trả $ 50,00 cho tất cả các tài khoản đó:

foreach (IPayable account in accountsToPay)
{
    account.Pay(50.00);
}

Vì vậy, bây giờ bạn thấy làm thế nào các giao diện là vô cùng hữu ích.

Chúng chỉ được sử dụng trên các đối tượng khởi tạo. Không phải trên các lớp tĩnh.

Nếu bạn đã thực hiện thanh toán tĩnh, khi lặp qua IPayable trong tài khoảnToPay, sẽ không có cách nào để biết liệu có nên gọi thanh toán trên BusinessAcount hoặc CustomerAccount hay không.


Chỉ vì các phương thức tĩnh không có ý nghĩa trong ví dụ NÀY, không có nghĩa là chúng không có ý nghĩa trong bất kỳ ví dụ nào. Trong ví dụ của bạn nếu giao diện IPayable có phương thức tĩnh "IncrementPayables" theo dõi số lượng khoản phải trả được thêm vào, đây sẽ là trường hợp sử dụng thực sự. Tất nhiên, người ta luôn có thể sử dụng một lớp trừu tượng, nhưng đó không phải là những gì bạn đã giải quyết. Ví dụ của bạn tự nó không làm suy yếu các phương thức tĩnh trong các giao diện.
Cruncher
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.