Tại sao các phương thức tĩnh không thể trừu tượng trong Java?


593

Câu hỏi là trong Java tại sao tôi không thể định nghĩa một phương thức tĩnh trừu tượng? ví dụ

abstract class foo {
    abstract void bar( ); // <-- this is ok
    abstract static void bar2(); //<-- this isn't why?
}

9
Vài lý do: phương thức tĩnh phải có phần thân ngay cả khi chúng là một phần của lớp trừu tượng vì người ta không cần tạo thể hiện của một lớp để truy cập phương thức tĩnh của nó. Một cách khác để suy nghĩ là nếu trong một khoảnh khắc chúng ta cho rằng nó được cho phép thì vấn đề là các cuộc gọi phương thức tĩnh không cung cấp bất kỳ Thông tin loại thời gian chạy (RTTI) nào, hãy nhớ rằng không cần tạo cá thể, do đó chúng không thể chuyển hướng để thực hiện overriden cụ thể của họ và do đó cho phép abstarct tĩnh không có ý nghĩa gì cả. Nói cách khác, nó không thể cung cấp bất kỳ lợi ích đa hình nào do đó không được phép.
sactiw

6
Nếu bạn có điều gì đó để trả lời câu hỏi, hãy đăng nó dưới dạng câu trả lời , không phải bình luận
Solomon Ucko

Câu trả lời:


568

Bởi vì "trừu tượng" có nghĩa là: "Thực hiện không có chức năng" và "tĩnh" có nghĩa là: "Có chức năng ngay cả khi bạn không có một đối tượng đối tượng". Và đó là một mâu thuẫn logic.


353
Một câu trả lời ngắn gọn hơn sẽ là 'thiết kế ngôn ngữ xấu'. Tĩnh có nghĩa là 'thuộc về lớp' bởi vì đó là cách nó được sử dụng bằng trực giác như chính câu hỏi này thể hiện. Xem "classmethod" trong Python.
Alexander Ljungberg

12
@Tomalak Tôi xin lỗi, tôi không rõ. Tất nhiên một phương thức tĩnh 'thuộc về lớp'. Tuy nhiên, nó chỉ có nghĩa là nó sống trong cùng một không gian tên. Một phương thức tĩnh không phải là một phương thức của chính đối tượng lớp: nó không hoạt động với 'this' là đối tượng lớp và nó không tham gia đúng vào chuỗi kế thừa. Nếu nó thực sự là một phương thức lớpabstract static sẽ có ý nghĩa hoàn hảo. Nó sẽ là một phương thức của chính đối tượng lớp mà các đối tượng lớp con phải thực hiện. Tất nhiên, cách mọi thứ diễn ra câu trả lời của bạn là chính xác mặc dù tôi không hiểu gì về ngôn ngữ.
Alexander Ljungberg

695
Đó không phải là một mâu thuẫn logic, đó là một thiếu sót về ngôn ngữ, nhiều ngôn ngữ khác hỗ trợ cho khái niệm này. "Trừu tượng" có nghĩa là "được thực hiện trong các lớp con", "tĩnh" có nghĩa là "được thực thi trên lớp chứ không phải là các thể hiện của lớp" Không có mâu thuẫn logic.
Eric Grange

10
@Eric: Và vẫn vậy , những gì bạn nói không áp dụng cho abstract static: Hàm X được "triển khai trong lớp con" không thể đồng thời được "thực thi trên lớp" - chỉ trên lớp con . Nơi đó không còn trừu tượng nữa.
Tomalak

72
@Tomakak: Logic của bạn là hình tròn. statickhông có nghĩa là "không trống rỗng" - đó chỉ là hậu quả của việc Java không cho phép các phương thức tĩnh trở nên trừu tượng. Nó có nghĩa là "có thể gọi được trên lớp." (Nó có nghĩa là " chỉ có thể gọi được trên lớp" nhưng đó là một vấn đề khác.) Nếu các abstract staticphương thức được Java hỗ trợ, tôi mong muốn nó có nghĩa là phương thức 1) phải được các lớp con triển khai và 2) là một phương thức lớp của lớp con. Một số phương thức không có ý nghĩa như các phương thức ví dụ. Thật không may, Java không cho phép bạn xác định rằng khi tạo một lớp cơ sở trừu tượng (hoặc một giao diện).
Michael Carman

326

Thiết kế ngôn ngữ kém. Sẽ hiệu quả hơn nhiều nếu gọi trực tiếp một phương thức trừu tượng tĩnh hơn là tạo một thể hiện chỉ để sử dụng phương thức trừu tượng đó. Đặc biệt đúng khi sử dụng một lớp trừu tượng như một cách giải quyết cho enum không có khả năng mở rộng, đó là một ví dụ thiết kế kém. Hy vọng họ giải quyết những hạn chế trong một phiên bản tiếp theo.


25
Java đầy những hạn chế kỳ lạ. Đóng chỉ truy cập các biến cuối cùng là một biến khác. Và danh sách còn lại là gần như vô tận. Công việc của một lập trình viên Java là phải biết họ và cách giải quyết của họ. Để vui chơi, chúng tôi phải sử dụng thứ gì đó tốt hơn Java trong thời gian rảnh rỗi. Nhưng tôi sẽ không bận tâm về. Đó là hạt giống để đạt được tiến bộ.
ceving

22
Tôi tin rằng những gì bạn gọi là "Thiết kế ngôn ngữ kém" thực sự giống với "Thiết kế ngôn ngữ bảo vệ", mục đích là để hạn chế các vi phạm nguyên tắc OO mà các lập trình viên làm do các tính năng ngôn ngữ không cần thiết.
ethanfar

22
Là khái niệm "tĩnh trừu tượng" có vi phạm nguyên tắc OO không?
Trevor

7
@threed, Không hề, nhưng tất nhiên có những người nói rằng khái niệm đơn thuần của staticnó đã là một sự vi phạm ....
Pacerier

4
Nhu cầu tĩnh là một minh chứng rõ ràng rằng "các nguyên tắc OO" không bao gồm tất cả như thường được tuyên bố.
Bến

147

Bạn không thể ghi đè một phương thức tĩnh, vì vậy làm cho nó trừu tượng sẽ là vô nghĩa. Hơn nữa, một phương thức tĩnh trong một lớp trừu tượng sẽ thuộc về lớp đó, và không phải là lớp ghi đè, vì vậy dù sao cũng không thể được sử dụng.


18
Vâng, nó thực sự là một sự xấu hổ bởi cách mà các phương thức tĩnh không thể bị ghi đè trong Java.
Michel

12
@Michel: điều gì sẽ là điểm? Nếu bạn muốn hành vi dựa trên cá thể, sử dụng các phương thức cá thể.
Ran Biron

8
Câu trả lời này không chính xác. Các phương thức tĩnh trong các lớp trừu tượng hoạt động tốt và thường được sử dụng. Chỉ là một phương thức tĩnh của chính lớp có thể không trừu tượng. @Michel không có nghĩa gì khi ghi đè một phương thức tĩnh. Nếu không có một thể hiện, làm thế nào thời gian chạy sẽ biết phương thức nào sẽ gọi?
erickson

63
@erickson - Ngay cả khi không có cá thể, hệ thống phân cấp lớp vẫn nguyên vẹn - kế thừa trên các phương thức tĩnh có thể hoạt động giống như kế thừa của các phương thức cá thể. Smalltalk làm điều đó, và nó khá hữu ích.
Jared

8
@matiasg không có gì mới cả. Các lớp trừu tượng luôn được phép có các phương thức tĩnh, không trừu tượng .
Matt Ball

70

Các abstractchú thích đến một phương pháp chỉ ra rằng phương pháp này PHẢI được overriden trong một lớp con.

Trong Java, một staticthành viên (phương thức hoặc trường) không thể bị ghi đè bởi các lớp con (điều này không nhất thiết đúng trong các ngôn ngữ hướng đối tượng khác, xem SmallTalk.) Một staticthành viên có thể bị ẩn , nhưng về cơ bản thì khác với ghi đè .

Vì các thành viên tĩnh không thể được ghi đè trong một lớp con, nên abstractchú thích có thể được áp dụng cho chúng.

Như một bên - các ngôn ngữ khác hỗ trợ kế thừa tĩnh, giống như kế thừa thể hiện. Từ góc độ cú pháp, các ngôn ngữ đó thường yêu cầu tên lớp được bao gồm trong câu lệnh. Ví dụ, trong Java, giả sử bạn đang viết mã trong ClassA, đây là các câu lệnh tương đương (nếu phương thứcA () là một phương thức tĩnh và không có phương thức cá thể nào có cùng chữ ký):

ClassA.methodA();

methodA();

Trong SmallTalk, tên lớp không phải là tùy chọn, vì vậy cú pháp là (lưu ý rằng SmallTalk không sử dụng. Để tách "chủ ngữ" và "động từ", mà thay vào đó sử dụng nó làm dấu kết thúc stHRend):

ClassA methodA.

Vì tên lớp luôn được yêu cầu, "phiên bản" chính xác của phương thức luôn có thể được xác định bằng cách duyệt qua hệ thống phân cấp lớp. Đối với những gì nó có giá trị, đôi khi tôi bỏ lỡ staticsự kế thừa và bị cắn bởi sự thiếu kế thừa tĩnh trong Java khi tôi lần đầu tiên bắt đầu với nó. Ngoài ra, SmallTalk được gõ vịt (và do đó không hỗ trợ theo hợp đồng theo chương trình.) Vì vậy, nó không có công cụ abstractsửa đổi cho các thành viên lớp.


1
"Một thành viên tĩnh không thể bị ghi đè bởi các lớp con" là sai. Có thể, ít nhất là trong Java6. Không chắc chắn khi nó là trường hợp.
Steven De Groote

15
@Steven De Groote Một thành viên tĩnh thực sự không thể bị ghi đè bởi các lớp con. Nếu một lớp con có một phương thức tĩnh có cùng chữ ký với một phương thức tĩnh trong siêu lớp, thì nó không ghi đè lên nó, nó sẽ ẩn nó. http://docs.oracle.com/javase/tutorial/java/IandI/override.html Sự khác biệt là tính đa hình chỉ hoạt động cho ghi đè, nhưng không cho các phương thức ẩn.
John29

2
@ John29 Cảm ơn bạn đã làm rõ, nhưng ngoài sự khác biệt về cách đặt tên, nó có vẻ tương tự trong cách sử dụng.
Steven De Groote

2
@Steven De Groote Có, cách sử dụng tương tự, nhưng hành vi thì khác. Đó là lý do tại sao không có phương pháp trừu tượng tĩnh - điểm của phương pháp trừu tượng tĩnh nếu chúng không hỗ trợ đa hình?
John29

3
@Steven De Groote: Sự khác biệt trở nên rõ ràng khi bạn có một cuộc gọi đến phương thức đó trong chính siêu lớp. Giả sử Super.foo gọi Super.bar. Nếu một lớp con thực hiện Sub class.bar, và sau đó gọi foo, thì foo vẫn sẽ gọi Super.bar, không phải Subgroup.bar. Do đó, những gì bạn thực sự có là hai phương pháp hoàn toàn khác nhau và không liên quan đến cả hai được gọi là "thanh". Đây không phải là ghi đè trong bất kỳ ý nghĩa hữu ích.
Doradus

14

Tôi cũng hỏi cùng một câu hỏi, đây là lý do tại sao

Vì lớp Trừu tượng nói, nó sẽ không thực hiện và cho phép lớp con cung cấp cho nó

Vì vậy, lớp con phải ghi đè lên các phương thức của Superclass,

Quy tắc số 1 - Một phương thức tĩnh không thể bị ghi đè

Bởi vì các thành viên và phương thức tĩnh là các phần tử thời gian biên dịch, đó là lý do tại sao Quá tải (Đa hình thời gian biên dịch) của các phương thức tĩnh được cho phép thay vì ghi đè (Đa hình thời gian chạy)

Vì vậy, họ không thể là Tóm tắt.

Không có thứ gì giống như tĩnh trừu tượng <--- Không được phép trong Vũ trụ Java


5
-1, Không đúng khi "Java không cho phép ghi đè phương thức tĩnh vì các thành viên tĩnh và phương thức là các phần tử thời gian biên dịch". Kiểm tra kiểu tĩnh chắc chắn là có thể với abstract staticxem stackoverflow.com/questions/370962/ . Các lý do thực sự tại sao Java không cho phép phương pháp tĩnh được ghi đè là do Java không cho phép phương pháp tĩnh được ghi đè.
Pacerier

Quá tải không liên quan gì đến đa hình. Quá tải và ghi đè không có gì chung ngoại trừ tiền tố "over" theo cách tương tự như Java và JavaScript xảy ra với cả hai đều có "Java". Tên của một phương thức không phải là danh tính của nó, đó là chữ ký của nó. vì vậy foo(String)không giống như foo(Integer)- đó là tất cả.
Thuyền trưởng Man

@CaptainMan Quá tải được gọi theo nghĩa đen là "đa hình tham số" bởi vì, tùy thuộc vào loại tham số, một phương thức khác được gọi là đa hình.
Thưởng thức

12

Đây là một thiết kế ngôn ngữ khủng khiếp và thực sự không có lý do tại sao nó không thể thực hiện được.

Trên thực tế, đây là một triển khai về cách nó thể được thực hiện trong JAVA :

public class Main {

        public static void main(String[] args) {
                // This is done once in your application, usually at startup
                Request.setRequest(new RequestImplementationOther());

                Request.doSomething();
        }

        public static final class RequestImplementationDefault extends Request {
                @Override
                void doSomethingImpl() {
                        System.out.println("I am doing something AAAAAA");
                }
        }

        public static final class RequestImplementaionOther extends Request {
                @Override
                void doSomethingImpl() {
                        System.out.println("I am doing something BBBBBB");
                }
        }

        // Static methods in here can be overriden
        public static abstract class Request {

                abstract void doSomethingImpl();

                // Static method
                public static void doSomething() {
                        getRequest().doSomethingImpl();
                }

                private static Request request;
                private static Request getRequest() {
                        // If setRequest is never called prior, it will default to a default implementation. Of course you could ignore that too. 
                        if ( request == null ) {
                                return request = new RequestImplementationDefault();
                        }
                        return request;
                }
                public static Request setRequest(Request r){
                        return request = r;
                }

        }
}

================= Ví dụ cũ bên dưới =================

Tìm kiếm getRequest và getRequestImpl ... setInstance có thể được gọi để thay đổi việc thực hiện trước khi cuộc gọi được thực hiện.

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * @author Mo. Joseph
 * @date 16 mar 2012
 **/

public abstract class Core {


    // ---------------------------------------------------------------        
    private static Core singleton; 
    private static Core getInstance() {
        if ( singleton == null )
            setInstance( new Core.CoreDefaultImpl() );  // See bottom for CoreDefaultImpl

        return singleton;
    }    

    public static void setInstance(Core core) {
        Core.singleton = core;
    }
    // ---------------------------------------------------------------        



    // Static public method
    public static HttpServletRequest getRequest() {      
        return getInstance().getRequestImpl();
    }


    // A new implementation would override this one and call setInstance above with that implementation instance
    protected abstract HttpServletRequest getRequestImpl();




    // ============================ CLASSES =================================

    // ======================================================================
    // == Two example implementations, to alter getRequest() call behaviour 
    // == getInstance() have to be called in all static methods for this to work
    // == static method getRequest is altered through implementation of getRequestImpl
    // ======================================================================

    /** Static inner class CoreDefaultImpl */
    public static class CoreDefaultImpl extends Core { 
        protected HttpServletRequest getRequestImpl() {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        }
    }

     /** Static inner class CoreTestImpl : Alternative implementation */
    public static class CoreTestImpl extends Core { 
        protected HttpServletRequest getRequestImpl() {
            return new MockedRequest();
        }
    }       

}

Được sử dụng như sau:

static {
     Core.setSingleton(new Core.CoreDefaultImpl());

     // Or

     Core.setSingleton(new Core.CoreTestImpl());

     // Later in the application you might use

     Core.getRequest(); 

}

6
Tôi không hiểu nơi bạn đã cung cấp một ví dụ về một abstract staticphương pháp như đã hỏi trong câu hỏi và bạn đã viết bằng chữ đậm CÓ THỂ ĐƯỢC LÀM TRONG JAVA . Điều này là hoàn toàn sai lầm.
Blip

1
Nó không cho phép bạn định nghĩa nó là tĩnh trừu tượng, nhưng bạn có thể đạt được kết quả tương tự, nhờ đó bạn có thể thay đổi việc thực hiện phương thức tĩnh bằng cách sử dụng mẫu / hack này. Nó hầu như không sai lầm. Nó có thể được thực hiện, nhưng sử dụng các ngữ nghĩa khác nhau.
mmm

Đây là một ví dụ về việc mở rộng một lớp trừu tượng, sau đó đặt các phương thức tĩnh vào con. Đây không phải là một ví dụ về CÓ THỂ LÀM ĐƯỢC TRONG JAVA, dưới bất kỳ hình thức nào.
Scuba Steve

2
@ScubaSteve Trước tiên, bạn đã sai về kết luận của mình. Thứ hai, nó đạt được kết quả tương tự. Có nghĩa là quyền truy cập tĩnh vào một lớp có thể được thay đổi bằng cách thực hiện khác. Đây không phải là một câu trả lời khi nói rằng tôi đã tạo từ khóa tĩnh có thể trừu tượng hóa, nhưng sử dụng mẫu này bạn có thể sử dụng các phương thức tĩnh và vẫn thay đổi cách triển khai của chúng. Mặc dù vậy, nó chỉ có ảnh hưởng tiêu cực đến toàn cầu, nhưng đối với môi trường test / prod / dev, nó thực sự là một mẹo nhỏ đối với chúng tôi.
mmm

5
  • Một phương thức trừu tượng chỉ được định nghĩa để nó có thể được ghi đè trong một lớp con. Tuy nhiên, các phương thức tĩnh không thể bị ghi đè. Do đó, đó là một lỗi thời gian biên dịch để có một phương thức tĩnh, trừu tượng.

    Bây giờ câu hỏi tiếp theo là tại sao các phương thức tĩnh không thể bị ghi đè ??

  • Đó là bởi vì các phương thức tĩnh thuộc về một lớp cụ thể chứ không thuộc về thể hiện của nó. Nếu bạn cố gắng ghi đè một phương thức tĩnh, bạn sẽ không gặp phải bất kỳ lỗi biên dịch hoặc thời gian chạy nào nhưng trình biên dịch sẽ chỉ ẩn phương thức tĩnh của siêu lớp.


4

Một phương pháp tĩnh, theo định nghĩa, không cần phải biết this. Vì vậy, nó không thể là một phương thức ảo (bị quá tải theo thông tin lớp con động có sẵn thông qua this); thay vào đó, quá tải phương thức tĩnh chỉ dựa trên thông tin có sẵn tại thời gian biên dịch (điều này có nghĩa là: một khi bạn tham khảo một phương thức tĩnh của siêu lớp, bạn gọi cụ thể là phương thức siêu lớp, nhưng không bao giờ là phương thức lớp con).

Theo đó, các phương thức tĩnh trừu tượng sẽ khá vô dụng vì bạn sẽ không bao giờ có tham chiếu của nó được thay thế bởi một số phần xác định.


4

Tôi thấy rằng đã có hàng trăm câu trả lời thần thánh nhưng tôi không thấy bất kỳ giải pháp thực tế nào. Tất nhiên đây là một vấn đề thực sự và không có lý do chính đáng để loại trừ cú pháp này trong Java. Vì câu hỏi ban đầu thiếu một bối cảnh mà điều này có thể cần, tôi cung cấp cả bối cảnh và giải pháp:

Giả sử bạn có một phương thức tĩnh trong một nhóm các lớp giống hệt nhau. Các phương thức này gọi một phương thức tĩnh là lớp cụ thể:

class C1 {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    private static void doMoreWork(int k) {
        // code specific to class C1
    }
}
class C2 {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    private static void doMoreWork(int k) {
        // code specific to class C2
    }
}

doWork()phương pháp trong C1C2giống hệt nhau. Có thể có rất nhiều các calsodes này: C3 C4vv Nếu static abstractđược phép, bạn sẽ loại bỏ mã trùng lặp bằng cách thực hiện một số thứ như:

abstract class C {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }

    static abstract void doMoreWork(int k);
}

class C1 extends C {
    private static void doMoreWork(int k) {
        // code for class C1
    }
}

class C2 extends C {
    private static void doMoreWork(int k) {
        // code for class C2
    }
}

nhưng điều này sẽ không được biên dịch vì static abstractsự kết hợp không được phép. Tuy nhiên, điều này có thể được phá vỡ bằng static classcấu trúc, được phép:

abstract class C {
    void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    abstract void doMoreWork(int k);
}
class C1 {
    private static final C c = new  C(){  
        @Override void doMoreWork(int k) {
            System.out.println("code for C1");
        }
    };
    public static void doWork() {
        c.doWork();
    }
}
class C2 {
    private static final C c = new C() {
        @Override void doMoreWork(int k) {
            System.out.println("code for C2");
        }
    };
    public static void doWork() {
        c.doWork();
    }
}

Với giải pháp này, mã duy nhất được sao chép là

    public static void doWork() {
        c.doWork();
    }

1
Vì trong lớp trừu tượng giải pháp cuối cùng của bạn, lớp C không có bất kỳ phương thức tĩnh nào, tại sao không để C1 và C2 mở rộng nó và ghi đè phương thức doMoreWork () và để bất kỳ lớp nào khác tạo ra thể hiện của nó và gọi các phương thức cần thiết. Về cơ bản, bạn đang thực hiện tương tự tức là mở rộng lớp C bằng cách sử dụng lớp ẩn danh và sau đó trong C1 và C2 bằng cách sử dụng thể hiện tĩnh của nó để cho phép truy cập từ bên trong một phương thức tĩnh nhưng điều này hoàn toàn không bắt buộc.
sactiw

Tôi không hiểu bối cảnh mà bạn đã cung cấp ở đây. Trong giải pháp cuối cùng của bạn, những gì bạn có thể làm là gọi C1.doWork()hoặc C2.doWork()Nhưng bạn không thể gọi C.doWork(). Cũng trong ví dụ mà bạn đã cung cấp, sẽ không hoạt động, giả sử nếu nó được cho phép, thì lớp sẽ Ctìm cách thực hiện như doMoreWork()thế nào? Cuối cùng tôi sẽ gọi mã bối cảnh của bạn là một thiết kế xấu. Tại sao? đơn giản vì bạn đã tạo một hàm riêng cho mã duy nhất thay vì tạo một hàm cho mã phổ biến và sau đó thực hiện một hàm tĩnh trong lớp C. Điều này dễ dàng hơn !!!
Blip

2

Giả sử có hai lớp, ParentChild. Parentabstract. Các khai báo như sau:

abstract class Parent {
    abstract void run();
}

class Child extends Parent {
    void run() {}
}

Điều này có nghĩa là bất kỳ trường hợp nào Parentphải chỉ định cách run()thực hiện.

Tuy nhiên, giả sử bây giờ Parentlà không abstract.

class Parent {
    static void run() {}
}

Điều này có nghĩa là Parent.run()sẽ thực thi phương thức tĩnh.

Định nghĩa của một abstractphương thức là "Một phương thức được khai báo nhưng không được thực hiện", có nghĩa là nó không trả về bất cứ thứ gì.

Định nghĩa của một staticphương thức là "Một phương thức trả về cùng một giá trị cho cùng các tham số bất kể trường hợp mà nó được gọi là".

abstractGiá trị trả về của một phương thức sẽ thay đổi khi thể hiện thay đổi. Một staticphương pháp sẽ không. Một static abstractphương thức gần như là một phương thức trong đó giá trị trả về là hằng số, nhưng không trả về bất cứ thứ gì. Đây là một mâu thuẫn logic.

Ngoài ra, thực sự không có nhiều lý do cho một static abstractphương pháp.


2

Một lớp trừu tượng không thể có một phương thức tĩnh bởi vì sự trừu tượng được thực hiện để đạt được BINDING NĂNG ĐỘNG trong khi các phương thức tĩnh được liên kết tĩnh với chức năng của chúng. Phương thức tĩnh có nghĩa là hành vi không phụ thuộc vào một biến đối tượng, do đó không yêu cầu đối tượng / đối tượng. Các phương thức tĩnh thuộc về lớp và không phải đối tượng. Chúng được lưu trữ trong một vùng nhớ được gọi là PERMGEN từ nơi nó được chia sẻ với mọi đối tượng. Các phương thức trong lớp trừu tượng được liên kết động với chức năng của chúng.


1
Các lớp trừu tượng chắc chắn có thể có các phương thức tĩnh. Nhưng không phải là phương pháp trừu tượng tĩnh.
JacksOnF1re

2

Khai báo một phương thức như staticlà phương tiện chúng ta có thể gọi phương thức đó bằng tên lớp của nó và nếu lớp đó abstractcũng vậy, sẽ không có ý nghĩa gì khi gọi nó vì nó không chứa bất kỳ phần thân nào, và do đó chúng ta không thể khai báo một phương thức cả staticabstract.


2

Vì các phương thức trừu tượng thuộc về lớp và không thể bị ghi đè bởi lớp triển khai. Ngay cả khi có một phương thức tĩnh có cùng chữ ký, nó sẽ ẩn phương thức đó, không ghi đè lên nó. Vì vậy, nó là không quan trọng để khai báo phương thức trừu tượng là tĩnh vì nó sẽ không bao giờ có được cơ thể. Vì vậy, biên dịch lỗi thời gian.


1

Một phương thức tĩnh có thể được gọi mà không cần một thể hiện của lớp. Trong ví dụ của bạn, bạn có thể gọi foo.bar2 (), nhưng không phải foo.bar (), vì đối với thanh bạn cần một thể hiện. Mã sau sẽ hoạt động:

foo var = new ImplementsFoo();
var.bar();

Nếu bạn gọi một phương thức tĩnh, nó sẽ được thực thi luôn cùng một mã. Trong ví dụ trên, ngay cả khi bạn xác định lại bar2 trong ImplementsFoo, một lệnh gọi tới var.bar2 () sẽ thực thi foo.bar2 ().

Nếu bar2 bây giờ không có triển khai (đó là ý nghĩa trừu tượng), bạn có thể gọi một phương thức mà không cần thực hiện. Điều đó rất có hại.


1
Và một phương thức tĩnh trừu tượng có thể được gọi mà không cần một thể hiện, nhưng nó sẽ yêu cầu một triển khai được tạo trong lớp con. Đó không phải là đa hình chính xác, nhưng cách duy nhất để khắc phục điều đó là để đứa trẻ cụ thể thực hiện một giao diện "yêu cầu" phương pháp "trừu tượng tĩnh". Lộn xộn, nhưng hoàn toàn khả thi.
fijiaaron

3
Thật ra, tôi đã sai. Bạn cũng không thể có các phương thức tĩnh trong một giao diện. Lỗ hổng ngôn ngữ.
fijiaaron

1

Tôi tin rằng tôi đã tìm thấy câu trả lời cho câu hỏi này, dưới dạng lý do tại sao các phương thức của giao diện (hoạt động giống như các phương thức trừu tượng trong lớp cha) không thể tĩnh. Đây là câu trả lời đầy đủ (không phải của tôi)

Về cơ bản các phương thức tĩnh có thể bị ràng buộc tại thời gian biên dịch, vì để gọi chúng, bạn cần chỉ định một lớp. Điều này khác với các phương thức cá thể, trong đó lớp tham chiếu mà bạn đang gọi phương thức có thể không xác định tại thời điểm biên dịch (do đó, khối mã nào được gọi chỉ có thể được xác định khi chạy).

Nếu bạn đang gọi một phương thức tĩnh, bạn đã biết lớp nơi nó được triển khai hoặc bất kỳ lớp con trực tiếp nào của nó. Nếu bạn xác định

abstract class Foo {
    abstract static void bar();
}

class Foo2 {
    @Override
    static void bar() {}
}

Sau đó, bất kỳ Foo.bar();cuộc gọi rõ ràng là bất hợp pháp, và bạn sẽ luôn luôn sử dụng Foo2.bar();.

Với ý nghĩ này, mục đích duy nhất của một phương thức trừu tượng tĩnh sẽ là bắt buộc các lớp con thực hiện một phương thức như vậy. Ban đầu bạn có thể nghĩ rằng điều này RẤT sai, nhưng nếu bạn có một tham số loại chung <E extends MySuperClass>thì sẽ rất tốt để đảm bảo thông qua giao diện Ecó thể .doSomething(). Hãy nhớ rằng do loại tổng quát xóa chỉ tồn tại ở thời gian biên dịch.

Vì vậy, nó sẽ hữu ích? Có, và có lẽ đó là lý do tại sao Java 8 cho phép các phương thức tĩnh trong các giao diện (mặc dù chỉ với cách triển khai mặc định). Tại sao không phải là các phương thức tĩnh trừu tượng với việc triển khai mặc định trong các lớp? Đơn giản là vì một phương thức trừu tượng với cách triển khai mặc định thực sự là một phương thức cụ thể.

Tại sao các phương thức tĩnh trừu tượng / giao diện không có triển khai mặc định? Rõ ràng, chỉ vì cách Java xác định khối mã nào nó phải thực thi (phần đầu tiên trong câu trả lời của tôi).


1

Bởi vì lớp trừu tượng là một khái niệm OOPS và các thành viên tĩnh không phải là một phần của OOPS ....
Bây giờ, điều chúng ta có thể khai báo các phương thức hoàn thành tĩnh trong giao diện và chúng ta có thể thực thi giao diện bằng cách khai báo phương thức chính bên trong một giao diện

interface Demo 
{
  public static void main(String [] args) {
     System.out.println("I am from interface");
  }
}

0

Ý tưởng có một phương thức tĩnh trừu tượng là bạn không thể sử dụng trực tiếp lớp trừu tượng cụ thể đó cho phương thức đó, nhưng chỉ có đạo hàm đầu tiên mới được phép thực hiện phương thức tĩnh đó (hoặc cho khái quát: lớp thực tế của chung chung bạn sử dụng).

Bằng cách đó, bạn có thể tạo ví dụ một lớp trừu tượng sortableObject hoặc thậm chí giao diện với các phương thức tĩnh trừu tượng (tự động), xác định các tham số của các tùy chọn sắp xếp:

public interface SortableObject {
    public [abstract] static String [] getSortableTypes();
    public String getSortableValueByType(String type);
}

Bây giờ bạn có thể định nghĩa một đối tượng có thể sắp xếp có thể được sắp xếp theo các loại chính giống nhau cho tất cả các đối tượng này:

public class MyDataObject implements SortableObject {
    final static String [] SORT_TYPES = {
        "Name","Date of Birth"
    }
    static long newDataIndex = 0L ;

    String fullName ;
    String sortableDate ;
    long dataIndex = -1L ;
    public MyDataObject(String name, int year, int month, int day) {
        if(name == null || name.length() == 0) throw new IllegalArgumentException("Null/empty name not allowed.");
        if(!validateDate(year,month,day)) throw new IllegalArgumentException("Date parameters do not compose a legal date.");
        this.fullName = name ;
        this.sortableDate = MyUtils.createSortableDate(year,month,day);
        this.dataIndex = MyDataObject.newDataIndex++ ;
    }
    public String toString() {
        return ""+this.dataIndex+". "this.fullName+" ("+this.sortableDate+")";
    }

    // override SortableObject 
    public static String [] getSortableTypes() { return SORT_TYPES ; }
    public String getSortableValueByType(String type) {
        int index = MyUtils.getStringArrayIndex(SORT_TYPES, type);
        switch(index) {
             case 0: return this.name ;
             case 1: return this.sortableDate ;
        }
        return toString(); // in the order they were created when compared
    }
}

Bây giờ bạn có thể tạo một

public class SortableList<T extends SortableObject> 

có thể truy xuất các loại, xây dựng menu bật lên để chọn loại để sắp xếp và sử dụng danh sách bằng cách lấy dữ liệu từ loại đó, cũng như chức năng thêm, khi loại đã được chọn, có thể tự động -ort các mục mới trong. Lưu ý rằng phiên bản của SortableList có thể truy cập trực tiếp vào phương thức tĩnh của "T":

String [] MenuItems = T.getSortableTypes();

Vấn đề với việc phải sử dụng một thể hiện là SortableList có thể chưa có các mục, nhưng đã cần cung cấp sắp xếp ưu tiên.

Cổ vũ, Olaf.


0

Đầu tiên, một điểm chính về các lớp trừu tượng - Một lớp trừu tượng không thể được khởi tạo (xem wiki ). Vì vậy, bạn không thể tạo bất kỳ trường hợp nào của một lớp trừu tượng.

Bây giờ, cách java xử lý các phương thức tĩnh bằng cách chia sẻ phương thức với tất cả các thể hiện của lớp đó.

Vì vậy, nếu bạn không thể khởi tạo một lớp, thì lớp đó không thể có các phương thức tĩnh trừu tượng vì một phương thức trừu tượng xin được mở rộng.

Bùng nổ.


Mọi phương thức trong một lớp trừu tượng đều cần phải mở rộng lớp của nó, vì vậy đây không phải là lý do. Bạn có thể mở rộng lớp trừu tượng (đồng thời, thực hiện phương thức trừu tượng). Vì vậy, điều này không làm việc như một lời giải thích.
Pere

0

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, cùng với các phương thức mặc định, các phương thức tĩnh cũng được cho phép trong một giao diện. Đ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.

Một ví dụ hay về điều này là:

list.sort(ordering);

thay vì

Collections.sort(list, ordering);

Một ví dụ khác về việc sử dụng các phương thức tĩnh cũng được đưa ra trong 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));
    }    
}

0

Bởi vì 'trừu tượng' có nghĩa là phương thức có nghĩa là bị ghi đè và người ta không thể ghi đè các phương thức 'tĩnh'.


Câu trả lời này không thêm gì mà các câu trả lời trước chưa được giải quyết.
MarsAtomic

@MarsAtomic Tôi nghĩ rằng nó phù hợp hơn thì câu trả lời được bình chọn hàng đầu. Ngoài ra, nó ngắn gọn.
Praveen Kumar

Sau đó, bạn có thể chỉnh sửa các câu trả lời trước đó để cải thiện chúng khi bạn có đủ đại diện. Tất cả những gì bạn đang làm là thêm nhiễu vào tín hiệu bằng cách tạo các câu trả lời trùng lặp. Vui lòng tuân theo các quy tắc và tập quán đã được thiết lập của Stack Overflow thay vì tạo riêng của bạn và mong mọi người khác tuân theo.
MarsAtomic

Tôi không đồng ý, xin vui lòng so sánh câu trả lời của tôi với người khác, đặc biệt là câu trả lời được bình chọn hàng đầu.
Praveen Kumar

"Tại sao tôi không thể làm điều này?" Trả lời: "bởi vì bạn không thể".
JacksOnF1re

0

Các phương thức thông thường có thể là trừu tượng khi chúng có nghĩa là bị ghi đè bởi các lớp con và được cung cấp chức năng. Hãy tưởng tượng lớp học Foođược mở rộng bởiBar1, Bar2, Bar3 v.v ... Vì vậy, mỗi lớp sẽ có phiên bản riêng của lớp trừu tượng theo nhu cầu của họ.

Bây giờ, các phương thức tĩnh theo định nghĩa thuộc về lớp, chúng không liên quan gì đến các đối tượng của lớp hoặc các đối tượng của các lớp con của nó. Họ thậm chí không cần chúng tồn tại, chúng có thể được sử dụng mà không cần khởi tạo các lớp. Do đó, chúng cần phải sẵn sàng hoạt động và không thể phụ thuộc vào các lớp con để thêm chức năng cho chúng.


0

Bởi vì trừu tượng là một từ khóa được áp dụng trên các phương thức Trừu tượng không chỉ định phần thân. Và nếu chúng ta nói về từ khóa tĩnh, nó thuộc về khu vực lớp.


xin vui lòng một chút chi tiết về câu trả lời của bạn.
Blip

0

bởi vì nếu bạn đang sử dụng bất kỳ thành viên tĩnh hoặc biến tĩnh nào trong lớp thì nó sẽ tải vào thời gian tải lớp.


3
và tại sao đó là một vấn đề?
eis

-1

Bạn có thể làm điều này với các giao diện trong Java 8.

Đây là tài liệu chính thức về nó:

https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html


2
Làm sao? Tôi đã tìm giải pháp và không thể tìm thấy bất kỳ.
thouliha

Có sao không Bạn không thể. Tất cả các phương thức giao diện tĩnh phải được gọi bằng lớp giao diện.
mmm

8
Câu trả lời này là sai và đang gây hiểu lầm cho những người mới trong java. Điều này không cho thấy bất kỳ ví dụ nào về phương pháp tĩnh trừu tượng vì nó không thể được thực hiện và nêu trong các câu trả lời khác
Blip

-1

Bởi vì nếu một lớp mở rộng một lớp trừu tượng thì nó phải ghi đè các phương thức trừu tượng và đó là bắt buộc. Và vì các phương thức tĩnh là các phương thức lớp được giải quyết tại thời gian biên dịch trong khi các phương thức được ghi đè là các phương thức thể hiện được giải quyết trong thời gian chạy và theo đa hình động.


Hãy tận dụng và chấm dứt mớ hỗn độn này.
Hầu tước Lorne
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.