ทำไมเมธอดสแตติกไม่สามารถเป็นนามธรรมใน Java ได้?


593

คำถามอยู่ใน Java ทำไมฉันไม่สามารถกำหนดวิธีคงที่นามธรรม? ตัวอย่างเช่น

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

9
เหตุผลไม่กี่: วิธีการคงที่จะต้องมีร่างกายแม้ว่าพวกเขาจะเป็นส่วนหนึ่งของระดับนามธรรมเพราะไม่จำเป็นต้องสร้างตัวอย่างของการเรียนการเข้าถึงวิธีการคงที่ของมัน อีกวิธีหนึ่งในการคิดเกี่ยวกับเรื่องนี้คือถ้าเราคิดว่าได้รับอนุญาตในขณะนั้นปัญหาก็คือการเรียกเมธอดแบบสแตติกไม่ได้ให้ข้อมูลประเภทเวลาเรียกใช้ (RTTI) โปรดจำไว้ว่าไม่จำเป็นต้องสร้างอินสแตนซ์ สำหรับการติดตั้งแบบ overriden ที่เฉพาะเจาะจงของพวกเขาและดังนั้นการอนุญาตให้ใช้ abstarct static ทำให้ไม่รู้สึกเลย กล่าวอีกนัยหนึ่งมันไม่สามารถให้ประโยชน์ที่หลากหลายได้ดังนั้นจึงไม่ได้รับอนุญาต
sactiw

6
หากคุณมีบางสิ่งบางอย่างที่จะตอบคำถามโพสต์มันเป็นคำตอบไม่ใช่ความคิดเห็น
โซโลมอน Ucko

คำตอบ:


568

เพราะ "นามธรรม" หมายถึง: "ไม่มีฟังก์ชั่นการใช้งาน" และ "คงที่" หมายถึง: "มีฟังก์ชั่นแม้ว่าคุณจะไม่มีอินสแตนซ์ของวัตถุ" และนั่นคือความขัดแย้งเชิงตรรกะ


353
คำตอบที่กระชับยิ่งขึ้นก็คือ 'การออกแบบภาษาที่ไม่ดี' คงที่ควรหมายถึง 'เป็นของชั้นเรียน' เพราะนั่นเป็นวิธีที่ใช้อย่างสังหรณ์ใจเมื่อคำถามนี้แสดงให้เห็น ดู "classmethod" ใน Python
Alexander Ljungberg

12
@ Tomalak ฉันขอโทษฉันไม่ชัดเจน แน่นอนว่าวิธีการคงที่ 'เป็นของชั้นเรียน' ถึงกระนั้นมันก็เป็นเพียงในแง่ที่ว่ามันอาศัยอยู่ใน namespace เดียวกัน วิธีการคงที่ไม่ใช่วิธีการของวัตถุคลาสตัวเอง: มันไม่ทำงานกับ 'นี้' เป็นวัตถุคลาสและมันไม่ได้มีส่วนร่วมอย่างถูกต้องในห่วงโซ่ของการสืบทอด ถ้ามันเป็นวิธีการเรียนอย่างแท้จริงabstract staticจะทำให้รู้สึกที่สมบูรณ์แบบ มันจะเป็นวิธีการของวัตถุคลาสตัวเองซึ่งวัตถุ subclass ต้องดำเนินการ แน่นอนว่าสิ่งต่าง ๆ ที่ยืนคำตอบของคุณถูกต้องแม้ฉันจะมีความเข้าใจเกี่ยวกับภาษา
Alexander Ljungberg

695
มันไม่ใช่ความขัดแย้งทางตรรกะมันเป็นภาษาที่สั้นภาษาอื่นหลายภาษาสนับสนุนแนวคิดนี้ "นามธรรม" หมายถึง "นำมาใช้ในคลาสย่อย", "คงที่" หมายถึง "ดำเนินการในชั้นเรียนมากกว่าอินสแตนซ์ชั้น" ไม่มีความขัดแย้งทางตรรกะ
Eric Grange

10
@ Eric: และยังคงเป็นสิ่งที่คุณพูดไม่ได้นำไปใช้กับabstract static: ฟังก์ชั่น X ที่จะ "ดำเนินการในประเภทรอง" ไม่สามารถ ที่ในเวลาเดียวกันจะ "ดำเนินการในชั้นเรียน" - เฉพาะในประเภทรอง ที่ซึ่งมันไม่เป็นนามธรรมอีกต่อไป
Tomalak

72
@ Tomakak: ตรรกะของคุณเป็นวงกลม staticไม่ได้หมายความว่า "ไม่ว่าง" - นั่นเป็นเพียงผลมาจาก Java ที่ไม่อนุญาตให้วิธีการแบบสแตติกเป็นนามธรรม มันหมายถึง "callable ในชั้นเรียน" (ควรจะหมายถึง "callable เฉพาะในชั้นเรียน" แต่นั่นเป็นปัญหาอื่น) หาก Java รองรับabstract staticวิธีที่ฉันคาดหวังว่าจะหมายถึงวิธีที่ 1) ต้องดำเนินการโดย subclasses และ 2) เป็นวิธีการเรียนของ subclass วิธีการบางอย่างก็ไม่สมเหตุสมผลเหมือนวิธีการของตัวอย่าง น่าเสียดายที่ Java ไม่อนุญาตให้คุณระบุว่าเมื่อสร้างคลาสฐานนามธรรม (หรืออินเทอร์เฟซ)
Michael Carman

326

การออกแบบภาษาไม่ดี มันจะมีประสิทธิภาพมากขึ้นในการเรียกใช้วิธีนามธรรมคงที่โดยตรงมากกว่าการสร้างตัวอย่างเพียงแค่ใช้วิธีนามธรรมนั้น จริงโดยเฉพาะอย่างยิ่งเมื่อใช้คลาสนามธรรมเป็นวิธีแก้ปัญหาสำหรับ enum ไม่สามารถขยายซึ่งเป็นอีกตัวอย่างการออกแบบที่ไม่ดี หวังว่าพวกเขาจะแก้ไขข้อ จำกัด เหล่านั้นในรุ่นถัดไป


25
Java เต็มไปด้วยข้อ จำกัด ที่แปลก การปิดการเข้าถึงเฉพาะตัวแปรสุดท้ายเท่านั้นเป็นอีกตัวแปรหนึ่ง และรายการที่เหลือเกือบจะไม่มีที่สิ้นสุด มันเป็นหน้าที่ของโปรแกรมเมอร์ Java ที่จะรู้จักพวกเขาและวิธีแก้ปัญหาของพวกเขา เพื่อให้มีความสนุกสนานเราต้องใช้สิ่งที่ดีกว่า Java ในเวลาว่าง แต่ฉันจะไม่ใส่ใจ นั่นคือเมล็ดพันธุ์ที่จะบรรลุความก้าวหน้า
ceving

22
ฉันเชื่อว่าสิ่งที่คุณอ้างถึงว่า "การออกแบบภาษาแย่" นั้นเป็นมากกว่า "การออกแบบภาษาป้องกัน" ซึ่งมีวัตถุประสงค์เพื่อ จำกัด การละเมิดหลักการ OO ที่โปรแกรมเมอร์ทำเนื่องจากคุณสมบัติด้านภาษาที่ไม่จำเป็น
ethanfar

22
แนวคิดของ "นามธรรมคงที่" เป็นการละเมิดหลักการ OO หรือไม่?
เทรเวอร์

7
@threed ไม่ได้ทั้งหมด แต่แน่นอนมีคนที่บอกว่าแนวคิดเพียงของstaticตัวเองอยู่แล้วละเมิด ....
Pacerier

4
ความต้องการสแตติกนั้นเป็นการสาธิตที่ชัดเจนว่า "หลักการ OO" นั้นไม่ได้ครอบคลุมทุกอย่างตามปกติ
Ben

147

คุณไม่สามารถแทนที่เมธอดสแตติกได้ดังนั้นการทำให้เป็นนามธรรมจะไม่มีความหมาย ยิ่งไปกว่านั้นวิธีการคงที่ในคลาสนามธรรมจะเป็นของคลาสนั้นไม่ใช่คลาสที่แทนที่ดังนั้นจึงไม่สามารถใช้งานได้


18
ใช่มันเป็นความอัปยศโดยวิธีการที่คงที่วิธีการไม่สามารถแทนที่ใน Java
Michel

12
@Michel: อะไรคือประเด็น หากคุณต้องการพฤติกรรมตามตัวอย่างใช้วิธีการอินสแตนซ์
Ran Biron

8
คำตอบนี้ไม่ถูกต้อง วิธีการคงที่ในชั้นเรียนนามธรรมทำงานได้ดีและใช้กันทั่วไป เป็นเพียงว่าวิธีการคงที่ของชั้นเรียนอาจไม่เป็นนามธรรม @Michel มันไม่สมเหตุสมผลที่จะแทนที่วิธีคงที่ หากไม่มีอินสแตนซ์รันไทม์จะรู้ได้อย่างไรว่าจะเรียกใช้วิธีใด
erickson

63
@erickson - แม้ไม่มีอินสแตนซ์ลำดับชั้นของชั้นจะไม่บุบสลาย - สืบทอดในวิธีการแบบคงที่สามารถทำงานได้เช่นเดียวกับการสืบทอดของวิธีการอินสแตนซ์ Smalltalk ทำและมีประโยชน์มาก
Jared

8
@matiasg ไม่มีอะไรใหม่เลย คลาสนามธรรมได้รับอนุญาตให้มีวิธีคงที่และไม่ใช่นามธรรมเสมอ
แมตต์บอล

70

การเพิ่มความคิดเห็นabstractไปยังเมธอดบ่งชี้ว่าเมธอดต้องถูกแทนที่ในคลาสย่อย

ใน Java เป็นstaticสมาชิก (วิธีการหรือสาขา) ไม่สามารถแทนที่โดย subclasses (นี้ไม่จำเป็นต้องเป็นจริงในภาษาเชิงวัตถุอื่นดู SmallTalk.) พบstaticสมาชิกอาจจะซ่อนอยู่แต่ที่เป็นพื้นฐานที่แตกต่างกว่าแทนที่

เนื่องจากสมาชิกแบบสแตติกไม่สามารถ overriden ในคลาสย่อยได้abstractคำอธิบายประกอบจึงไม่สามารถใช้ได้กับพวกเขา

ในขณะที่ภาษาอื่นรองรับการสืบทอดแบบคงที่เช่นเดียวกับการสืบทอดอินสแตนซ์ จากมุมมองของไวยากรณ์ภาษาเหล่านั้นมักจะต้องการชื่อคลาสที่จะรวมอยู่ในคำสั่ง ตัวอย่างเช่นใน Java สมมติว่าคุณกำลังเขียนโค้ดใน ClassA นี่เป็นข้อความสั่งที่เทียบเท่ากัน (หาก methodA () เป็นวิธีคงที่และไม่มีวิธีอินสแตนซ์ที่มีลายเซ็นเดียวกัน):

ClassA.methodA();

และ

methodA();

ใน SmallTalk ชื่อคลาสนั้นไม่จำเป็นดังนั้นไวยากรณ์คือ (โปรดทราบว่า SmallTalk ไม่ได้ใช้. เพื่อแยก "subject" และ "verb" แต่ใช้แทนเป็นเทอร์มิเนล statemend):

ClassA methodA.

เนื่องจากชื่อคลาสจำเป็นต้องมีเสมอ "รุ่น" ของวิธีการที่ถูกต้องสามารถกำหนดได้เสมอโดยการข้ามผ่านลำดับชั้นของคลาส สำหรับสิ่งที่มีค่าฉันจะพลาดการstaticรับมรดกเป็นครั้งคราวและถูกกัดโดยขาดการสืบทอดแบบคงที่ใน Java เมื่อฉันเริ่มต้นด้วย นอกจากนี้ SmallTalk จะพิมพ์ด้วยเป็ด (ดังนั้นจึงไม่สนับสนุนการทำสัญญาโดยโปรแกรม) ดังนั้นจึงไม่มีการabstractแก้ไขสำหรับสมาชิกของคลาส


1
"สมาชิกแบบสแตติกไม่สามารถถูกแทนที่ด้วยคลาสย่อย" ผิด เป็นไปได้อย่างน้อยใน Java6 ไม่แน่ใจตั้งแต่เมื่อเป็นเช่นนั้น
Steven De Groote

15
@Steven De Groote สมาชิกแบบสแตติกไม่สามารถถูกแทนที่ด้วยคลาสย่อยได้ หากคลาสย่อยมีเมธอดสแตติกที่มีลายเซ็นเดียวกันกับเมธอดสแตติกในซูเปอร์คลาสนั้นจะไม่แทนที่มันก็จะซ่อนมัน http://docs.oracle.com/javase/tutorial/java/IandI/override.htmlความแตกต่างคือ polymorphism ใช้งานได้สำหรับการเขียนทับเท่านั้น แต่ไม่ใช่วิธีซ่อนเร้น
John29

2
@ John29 ขอบคุณสำหรับความกระจ่าง แต่นอกเหนือจากความแตกต่างของการตั้งชื่อดูเหมือนว่าในการใช้งาน
Steven De Groote

2
@Steven De Groote ใช่มันคล้ายกับการใช้งาน แต่พฤติกรรมแตกต่างกัน นั่นคือเหตุผลที่ทำไมไม่มีวิธีนามธรรมแบบคงที่ - จุดของวิธีนามธรรมแบบคงที่คือถ้าพวกเขาไม่สนับสนุนความแตกต่าง?
John29

3
@Steven De Groote: ความแตกต่างจะชัดเจนขึ้นเมื่อคุณมีการเรียกใช้เมธอดนั้นในซูเปอร์คลาสเอง สมมติว่า Super.foo เรียก Super.bar หากคลาสย่อยใช้ Subclass.bar จากนั้นเรียก foo ดังนั้น foo จะยังคงเรียก Super.bar ไม่ใช่ Subclass.bar ดังนั้นสิ่งที่คุณมีจริงๆคือสองวิธีที่แตกต่างกันอย่างสิ้นเชิงและไม่เกี่ยวข้องกันทั้งสองเรียกว่า "บาร์" สิ่งนี้ไม่ได้ถูกแทนที่ด้วยความรู้สึกที่เป็นประโยชน์ใด ๆ
Doradus

14

ฉันยังถามคำถามเดียวกันนี่คือเหตุผล

ตั้งแต่ชั้นนามธรรมพูดว่ามันจะไม่ให้การดำเนินการและอนุญาตให้ subclass เพื่อให้มัน

ดังนั้นซับคลาสต้องแทนที่เมธอดของ Superclass

กฎข้อที่ 1 - วิธีการแบบคงที่ไม่สามารถเขียนทับได้

เนื่องจากสมาชิกและวิธีการแบบคงที่เป็นองค์ประกอบเวลารวบรวมนั่นคือเหตุผลที่การโอเวอร์โหลด (Polile time Polymorphism) ของวิธีการแบบสแตติกได้รับอนุญาตให้ใช้งานมากกว่าการแทนที่ (Runtime Polymorphism)

ดังนั้นพวกเขาไม่สามารถเป็นนามธรรมได้

ไม่มีสิ่งใดเหมือนนามธรรมแบบคงที่ <--- ไม่อนุญาตใน Java Universe


5
-1 มันไม่เป็นความจริงที่ "Java ไม่อนุญาตให้ใช้วิธีการแบบสแตติกแทนที่เนื่องจากสมาชิกแบบคงที่และวิธีการเป็นองค์ประกอบเวลารวบรวม" คงที่ประเภทการตรวจสอบเป็นไปได้แน่นอนกับabstract staticเห็นstackoverflow.com/questions/370962/... เหตุผลที่แท้จริงว่าทำไม Java ไม่อนุญาตให้มีวิธีการแบบคงที่จะแทนที่เป็นเพราะ Java ไม่อนุญาตให้มีวิธีการแบบคงที่จะแทนที่
Pacerier

การบรรทุกเกินพิกัดไม่เกี่ยวข้องกับความหลากหลาย การโหลดมากเกินไปและการแทนที่ไม่มีอะไรเหมือนกันยกเว้นคำนำหน้า "เกิน" ในลักษณะเดียวกับที่ Java และ JavaScript เกิดขึ้นกับทั้งคู่มี "Java" อยู่ในตัว ชื่อของวิธีการไม่ได้เป็นตัวตนมันเป็นลายเซ็นของมัน ดังนั้นจึงfoo(String)ไม่เหมือนกับfoo(Integer)- นั่นคือทั้งหมดที่มันเป็น
Captain Man

@CaptainMan การบรรทุกเกินพิกัดเรียกว่า "parametric polymorphism" อย่างแท้จริงเพราะขึ้นอยู่กับประเภทของพารามิเตอร์วิธีการที่แตกต่างกันเรียกว่าซึ่งเป็น polymorphism
Davor

12

นี่คือการออกแบบภาษาที่แย่มากและไม่มีเหตุผลว่าทำไมถึงเป็นไปไม่ได้

ในความเป็นจริงนี่คือการดำเนินการเกี่ยวกับวิธีการที่สามารถทำได้ใน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;
                }

        }
}

================= ตัวอย่างเก่าด้านล่าง =================

ค้นหา getRequest และสามารถเรียกใช้ getRequestImpl ... setInstance เพื่อปรับเปลี่ยนการใช้งานก่อนที่จะทำการโทร

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

}

ใช้ดังต่อไปนี้:

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

     // Or

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

     // Later in the application you might use

     Core.getRequest(); 

}

6
ฉันไม่เข้าใจที่คุณให้ตัวอย่างของabstract staticวิธีการตามที่ถามในคำถามและคุณเขียนเป็นตัวหนาสามารถทำได้ในJAVA นี่เป็นการเข้าใจผิดอย่างสมบูรณ์
Blip

1
มันไม่ได้ช่วยให้คุณสามารถกำหนดเป็นนามธรรมคงที่ แต่คุณสามารถบรรลุผลที่คล้ายกันโดยที่คุณสามารถเปลี่ยนการใช้งานของวิธีการคงที่โดยใช้รูปแบบ / แฮ็คนี้ มันแทบจะเข้าใจผิด สามารถทำได้ แต่ใช้ความหมายที่แตกต่างกัน
mmm

นี่คือตัวอย่างของการขยายคลาสนามธรรมจากนั้นวางเมธอดสแตติกในเด็ก นี่ไม่ใช่ตัวอย่างของ CAN CAN IN JAVA ไม่ว่าด้วยวิธีใดก็ตาม
Scuba Steve

2
@ ScubaSteve ก่อนอื่นคุณคิดผิด ประการที่สองมันบรรลุผลเดียวกัน ความหมายคงที่การเข้าถึงคลาสสามารถเปลี่ยนแปลงได้โดยการใช้งานอื่น มันไม่ได้เป็นคำตอบที่บอกว่าฉันทำคำหลักคงที่เป็นนามธรรม แต่ใช้รูปแบบนี้คุณสามารถใช้วิธีการคงที่และยังคงเปลี่ยนการใช้งานของพวกเขา มันมีผลกระทบเชิงลบจากการเป็นโลกเพียงอย่างเดียว แต่สำหรับสภาพแวดล้อมการทดสอบ / แยงซอฟ / dev นั้นจะเป็นการหลอกลวงสำหรับเรา
mmm

5
  • วิธีนามธรรมถูกกำหนดไว้เท่านั้นเพื่อให้สามารถแทนที่ในคลาสย่อย อย่างไรก็ตามวิธีการคงที่ไม่สามารถแทนที่ ดังนั้นจึงเป็นข้อผิดพลาดในการคอมไพล์เพื่อให้มีวิธีนามธรรมแบบคงที่

    ตอนนี้คำถามต่อไปคือเหตุผลที่ว่าทำไมวิธีการแบบคงที่ไม่สามารถเขียนทับได้?

  • เป็นเพราะวิธีการคงที่เป็นของชั้นเรียนเฉพาะและไม่ได้เป็นตัวอย่าง หากคุณพยายามแทนที่เมธอดสแตติกคุณจะไม่ได้รับการรวบรวมหรือข้อผิดพลาดรันไทม์ แต่คอมไพเลอร์จะซ่อนเมธอดแบบคงที่ของซูเปอร์คลาส


4

thisวิธีการแบบคงที่โดยความหมายไม่จำเป็นต้องรู้ ดังนั้นจึงไม่สามารถเป็นวิธีเสมือน (ที่มีการโอเวอร์โหลดตามข้อมูลคลาสย่อยแบบไดนามิกที่มีให้ผ่านthis); แทนวิธีคงที่เกินพิกัดจะขึ้นอยู่กับข้อมูลที่มีอยู่ในเวลารวบรวมเท่านั้น (ซึ่งหมายความว่า: เมื่อคุณอ้างถึงวิธีการคงที่ของซูเปอร์คลาสคุณจะเรียกว่าวิธีการซูเปอร์คลาส แต่ไม่เคยเป็นวิธีการคลาสย่อย)

ตามนี้วิธีคงที่นามธรรมจะค่อนข้างไร้ประโยชน์เพราะคุณจะไม่เคยมีการอ้างอิงแทนโดยร่างกายที่กำหนดบางอย่าง


4

ฉันเห็นว่ามีคำตอบ god-zillion แล้ว แต่ฉันไม่เห็นวิธีแก้ปัญหาการปฏิบัติใด ๆ แน่นอนว่านี่เป็นปัญหาที่แท้จริงและไม่มีเหตุผลที่ดีสำหรับการยกเว้นไวยากรณ์นี้ใน Java เนื่องจากคำถามเดิมไม่มีบริบทที่อาจจำเป็นฉันจึงให้ทั้งบริบทและวิธีการแก้ไข:

สมมติว่าคุณมีวิธีการคงที่ในกลุ่มของชั้นเรียนที่เหมือนกัน เมธอดเหล่านี้เรียกเมธอดสแตติกที่เป็นคลาสเฉพาะ:

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()วิธีการในC1และC2เหมือนกัน อาจมี calsses เหล่านี้จำนวนมาก: C3 C4ฯลฯ หากstatic abstractได้รับอนุญาตคุณต้องกำจัดรหัสซ้ำโดยทำสิ่งต่อไปนี้:

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
    }
}

แต่สิ่งนี้จะไม่รวบรวมเพราะstatic abstractไม่อนุญาตให้รวมกัน อย่างไรก็ตามสิ่งนี้สามารถหลีกเลี่ยงได้ด้วยการstatic classสร้างซึ่งได้รับอนุญาต:

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

ด้วยวิธีนี้รหัสเดียวที่ซ้ำกันคือ

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

1
เนื่องจากในโซลูชันสุดท้ายของคุณคลาสนามธรรม C ไม่มีวิธีแบบสแตติกทำไมไม่ให้ C1 และ C2 ขยายและแทนที่วิธี doMoreWork () และให้คลาสอื่นสร้างอินสแตนซ์และเรียกใช้เมธอดที่ต้องการ โดยทั่วไปคุณกำลังทำแบบเดียวกันนั่นคือการขยายคลาส C โดยใช้คลาสที่ไม่ระบุชื่อจากนั้นใน C1 และ C2 โดยใช้อินสแตนซ์แบบคงที่เพื่อให้สามารถเข้าถึงจากภายในวิธีแบบคงที่ แต่ไม่จำเป็นเลย
sactiw

ฉันไม่เข้าใจบริบทที่คุณให้ไว้ที่นี่ ในโซลูชันสุดท้ายของคุณสิ่งที่คุณสามารถทำได้คือการโทรC1.doWork()หรือC2.doWork()แต่คุณไม่สามารถโทรC.doWork()ได้ นอกจากนี้ในตัวอย่างที่คุณให้ไว้ซึ่งไม่สามารถใช้งานได้ถ้าสมมติว่าได้รับอนุญาตแล้วชั้นเรียนจะCหาวิธีการนำไปปฏิบัติdoMoreWork()อย่างไร ในที่สุดฉันจะเรียกรหัสบริบทของคุณการออกแบบที่ไม่ดี ทำไม? Cเพียงเพราะคุณได้สร้างฟังก์ชั่นที่แยกต่างหากสำหรับรหัสที่เป็นเอกลักษณ์แทนการสร้างฟังก์ชั่นสำหรับรหัสที่เป็นปกติแล้วการใช้ฟังก์ชั่นคงที่ในชั้นเรียน มันง่ายกว่า !!!
Blip

2

สมมติว่ามีสองคลาสParentและChild. เป็นParent abstractการประกาศมีดังนี้:

abstract class Parent {
    abstract void run();
}

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

ซึ่งหมายความว่าอินสแตนซ์ใด ๆ ของParentต้องระบุวิธีrun()การดำเนินการ

แต่สมมติว่าตอนนี้ไม่ได้Parentabstract

class Parent {
    static void run() {}
}

ซึ่งหมายความว่าParent.run()จะดำเนินการวิธีการคงที่

คำจำกัดความของabstractวิธีการคือ "วิธีการที่ประกาศ แต่ไม่ได้ใช้" ซึ่งหมายความว่าจะไม่ส่งคืนสิ่งใดเอง

คำจำกัดความของstaticวิธีการคือ "วิธีการที่ส่งกลับค่าเดียวกันสำหรับพารามิเตอร์เดียวกันโดยไม่คำนึงถึงอินสแตนซ์ที่มันถูกเรียกว่า"

abstractค่าตอบแทนวิธีการจะมีการเปลี่ยนแปลงการเปลี่ยนแปลงเช่น staticวิธีจะไม่ static abstractวิธีสวยมากวิธีการที่ค่าตอบแทนเป็นค่าคงที่ แต่ไม่กลับอะไร นี่คือความขัดแย้งทางตรรกะ

นอกจากนี้ยังมีเหตุผลไม่มากสำหรับstatic abstractวิธีการ


2

คลาสนามธรรมไม่สามารถมีวิธีแบบสแตติกได้เนื่องจากมีการทำนามธรรมเพื่อให้บรรลุการเชื่อมโยงแบบไดนามิกในขณะที่วิธีการแบบคงที่ถูกผูกไว้กับการทำงานของพวกเขาวิธีการแบบคงที่หมายถึงพฤติกรรมที่ไม่ขึ้นอยู่กับตัวแปรอินสแตนซ์ วิธีการคงที่เป็นของชั้นเรียนและไม่วัตถุ พวกเขาจะถูกเก็บไว้ในพื้นที่หน่วยความจำที่รู้จักกันเป็น PERMGEN จากที่มันถูกใช้ร่วมกับทุกวัตถุ วิธีการในชั้นนามธรรมจะผูกกับการทำงานของพวกเขาแบบไดนามิก


1
คลาสนามธรรมสามารถมีวิธีการแบบคงที่ แต่ไม่ใช่วิธีนามธรรมคงที่
JacksOnF1re

2

ประกาศวิธีการเป็นstaticวิธีการที่เราสามารถเรียกวิธีการว่าด้วยชื่อชั้นและถ้าระดับที่เป็นabstractเช่นกันมันทำให้รู้สึกไม่ที่จะเรียกมันขณะที่มันไม่ได้มีตัวใดและด้วยเหตุนี้เราไม่สามารถประกาศวิธีการที่ทั้งสองเป็นและstaticabstract


2

เนื่องจากเมธอด abstract เป็นของคลาสและไม่สามารถถูกแทนที่โดยคลาสที่นำไปใช้แม้ว่าจะมีเมธอดแบบสแตติกที่มีลายเซ็นเหมือนกันมันจะซ่อนเมธอดไม่แทนที่มัน ดังนั้นจึงไม่มีสาระสำคัญที่จะประกาศวิธีนามธรรมเป็นแบบคงที่ไม่เคยได้รับร่างกายดังนั้นข้อผิดพลาดเวลารวบรวม


1

วิธีการคงที่สามารถเรียกได้โดยไม่มีตัวอย่างของชั้นเรียน ในตัวอย่างของคุณคุณสามารถโทร foo.bar2 () แต่ไม่ใช่ foo.bar () เพราะสำหรับแถบที่คุณต้องการอินสแตนซ์ รหัสต่อไปนี้จะใช้งานได้:

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

หากคุณเรียกใช้วิธีการคงที่มันจะถูกดำเนินการเสมอรหัสเดียวกัน ในตัวอย่างข้างต้นแม้ว่าคุณจะกำหนด bar2 ใหม่ใน ImplementsFoo การเรียกไปที่ var.bar2 () จะเรียกใช้งาน foo.bar2 ()

ถ้า bar2 ไม่มีการนำไปใช้ (นั่นคือความหมายเชิงนามธรรม) คุณสามารถเรียกใช้เมธอดได้โดยไม่ต้องติดตั้ง นั่นเป็นอันตรายมาก


1
และวิธีการแบบคงที่นามธรรมสามารถเรียกได้โดยไม่มีตัวอย่าง แต่มันจะต้องมีการดำเนินการที่จะสร้างขึ้นในชั้นเรียนของเด็ก มันไม่ได้เป็นพหุนามอย่างแน่นอน แต่วิธีเดียวที่จะหลีกเลี่ยงมันได้ก็คือให้เด็กที่เป็นรูปธรรมใช้อินเทอร์เฟซที่ "ต้องการ" วิธี "นามธรรมคงที่" ยุ่ง แต่ใช้การได้
fijiaaron

3
ที่จริงฉันผิด คุณไม่สามารถมีวิธีการคงที่ในส่วนต่อประสาน ข้อบกพร่องด้านภาษา
fijiaaron

1

ฉันเชื่อว่าฉันได้พบคำตอบสำหรับคำถามนี้ในรูปแบบของวิธีการอินเทอร์เฟซของ (ซึ่งทำงานเช่นวิธีนามธรรมในระดับผู้ปกครอง) ไม่สามารถคงที่ นี่คือคำตอบแบบเต็ม (ไม่ใช่ของฉัน)

โดยทั่วไปวิธีการคงที่สามารถถูกผูกไว้ในเวลารวบรวมตั้งแต่การเรียกพวกเขาคุณต้องระบุชั้นเรียน ซึ่งแตกต่างจากวิธีการแบบอินสแตนซ์ซึ่งคลาสของการอ้างอิงที่คุณกำลังเรียกวิธีอาจไม่เป็นที่รู้จักในเวลารวบรวม

หากคุณกำลังเรียกใช้เมธอดแบบสแตติกคุณจะทราบคลาสที่ใช้งานหรือคลาสย่อยโดยตรงของคลาสนั้น หากคุณกำหนด

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

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

แล้วใด ๆโทรเป็นสิ่งผิดกฎหมายอย่างเห็นได้ชัดและคุณก็จะใช้Foo.bar();Foo2.bar();

เมื่อคำนึงถึงสิ่งนี้จุดประสงค์เพียงอย่างเดียวของวิธีนามธรรมแบบสแตติกคือการบังคับใช้คลาสย่อยเพื่อใช้วิธีดังกล่าว ในขั้นต้นคุณอาจคิดว่านี่เป็นสิ่งที่ผิดมาก แต่ถ้าคุณมีพารามิเตอร์ประเภททั่วไป<E extends MySuperClass>มันจะดีที่จะรับประกันผ่านอินเตอร์เฟซที่สามารถE .doSomething()โปรดทราบว่าเนื่องจากประเภทการลบข้อมูลทั่วไปมีอยู่ในเวลารวบรวมเท่านั้น

ดังนั้นมันจะมีประโยชน์หรือไม่ ใช่และนั่นอาจเป็นเหตุผลว่าทำไม Java 8 จึงอนุญาตให้ใช้เมธอดแบบคงที่ในส่วนต่อประสาน ทำไมไม่ใช้วิธีแบบคงที่นามธรรมที่มีการใช้งานเริ่มต้นในชั้นเรียน? เพียงเพราะวิธีนามธรรมที่มีการใช้งานเริ่มต้นเป็นจริงวิธีการที่เป็นรูปธรรม

ทำไมไม่ใช้วิธีแบบนามธรรม / ส่วนต่อประสานที่ไม่มีการใช้งานเริ่มต้น เห็นได้ชัดว่าเป็นเพราะวิธีการที่จาวาระบุว่าการบล็อกโค้ดนั้นจะต้องดำเนินการอย่างไร (ส่วนแรกของคำตอบของฉัน)


1

เนื่องจากคลาสนามธรรมเป็นแนวคิด OOPS และสมาชิกแบบสแตติกไม่ได้เป็นส่วนหนึ่งของ OOPS ....
ตอนนี้สิ่งที่เราสามารถประกาศวิธีการแบบคงที่สมบูรณ์ในส่วนต่อประสานและเราสามารถดำเนินการส่วนต่อประสานโดยประกาศวิธีหลักภายในอินเทอร์เฟซ

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

0

แนวคิดของการมีวิธีนามธรรมแบบคงที่คือคุณไม่สามารถใช้คลาสนามธรรมเฉพาะนั้นได้โดยตรงสำหรับวิธีนั้น แต่มีเพียงอนุพันธ์แรกเท่านั้นที่ได้รับอนุญาตให้ใช้วิธีแบบคงที่นั้น (หรือสำหรับ generics: คลาสจริงของสามัญคุณ ใช้).

ด้วยวิธีนี้คุณสามารถสร้างตัวอย่างคลาสนามธรรม sortableObject หรืออินเทอร์เฟซด้วย (อัตโนมัติ -) วิธีคงที่นามธรรมซึ่งกำหนดพารามิเตอร์ของตัวเลือกการจัดเรียง:

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

ตอนนี้คุณสามารถกำหนดวัตถุที่สามารถเรียงลำดับได้ซึ่งสามารถจัดเรียงตามประเภทหลักที่เหมือนกันสำหรับวัตถุเหล่านี้ทั้งหมด:

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
    }
}

ตอนนี้คุณสามารถสร้าง

public class SortableList<T extends SortableObject> 

ที่สามารถดึงประเภทสร้างเมนูป๊อปอัพเพื่อเลือกประเภทที่จะเรียงลำดับและใช้รายการโดยรับข้อมูลจากประเภทนั้นรวมทั้ง hainv ฟังก์ชั่นเพิ่มที่เมื่อเลือกประเภทการเรียงสามารถอัตโนมัติ - เรียงรายการใหม่ ๆ โปรดทราบว่าอินสแตนซ์ของ SortableList สามารถเข้าถึงวิธีการคงที่ของ "T" โดยตรง:

String [] MenuItems = T.getSortableTypes();

ปัญหาเกี่ยวกับการใช้อินสแตนซ์คือ SortableList อาจยังไม่มีไอเท็ม แต่จำเป็นต้องมีการจัดเรียงที่ต้องการอยู่แล้ว

Cheerio, Olaf


0

ก่อนอื่นประเด็นสำคัญเกี่ยวกับคลาสนามธรรม - คลาสนามธรรมไม่สามารถสร้างอินสแตนซ์ได้ (ดูวิกิ ) ดังนั้นคุณไม่สามารถสร้างใด ๆตัวอย่างของระดับนามธรรม

ตอนนี้วิธีที่จาวาเกี่ยวข้องกับวิธีการคงที่คือการแบ่งปันวิธีการกับอินสแตนซ์ทั้งหมดของชั้นเรียนที่

ดังนั้นหากคุณไม่สามารถยกตัวอย่างคลาสได้คลาสนั้นจะไม่มีเมธอดสแตติกแบบนามธรรมเนื่องจากเมธอดแบบนามธรรมจะถูกขยายออกไป

ความเจริญ


ทุกวิธีในคลาสนามธรรมจำเป็นต้องขยายคลาสของมันเพื่อดำเนินการต่อดังนั้นนี่ไม่ใช่ข้อแก้ตัว คุณสามารถขยายคลาสนามธรรม (ในเวลาเดียวกันโดยใช้วิธีนามธรรม) ดังนั้นนี่ไม่ได้เป็นคำอธิบาย
Pere

0

ตามเอกสาร Java :

วิธีการคงที่เป็นวิธีการที่เกี่ยวข้องกับชั้นเรียนที่มีการกำหนดมากกว่าที่จะมีวัตถุใด ๆ ทุกอินสแตนซ์ของคลาสแบ่งปันวิธีการแบบคงที่

ใน Java 8 พร้อมกับเมธอดดีฟอลต์เมธอดสแตติกจะอนุญาตในอินเตอร์เฟส สิ่งนี้ทำให้ง่ายขึ้นสำหรับเราในการจัดระเบียบวิธีการช่วยเหลือในห้องสมุดของเรา เราสามารถเก็บวิธีการแบบคงที่เฉพาะกับอินเทอร์เฟซในอินเทอร์เฟซเดียวกันมากกว่าในคลาสแยกต่างหาก

ตัวอย่างที่ดีของสิ่งนี้คือ:

list.sort(ordering);

แทน

Collections.sort(list, ordering);

ตัวอย่างของการใช้วิธีการคงที่อีกอย่างหนึ่งก็คือใน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

เพราะ 'นามธรรม' หมายถึงวิธีการที่มีขึ้นเพื่อแทนที่และหนึ่งไม่สามารถแทนที่วิธีการ 'คงที่'


คำตอบนี้ไม่ได้เพิ่มสิ่งที่คำตอบก่อนหน้านี้ยังไม่ได้รับการแก้ไข
MarsAtomic

@ MarsAtomic ฉันคิดว่ามันเหมาะสมกว่าแล้วคำตอบที่ได้รับคะแนนสูงสุด นอกจากนี้มันรวบรัด
Praveen Kumar

จากนั้นคุณสามารถแก้ไขคำตอบก่อนหน้าเพื่อปรับปรุงพวกเขาเมื่อคุณมีตัวแทนที่เพียงพอ สิ่งที่คุณทำคือการเพิ่มสัญญาณรบกวนให้กับสัญญาณโดยการสร้างคำตอบที่ซ้ำกัน โปรดปฏิบัติตามกฎและประเพณีที่กำหนดไว้ของ Stack Overflow ไม่ใช่สร้างเป็นของคุณเองและคาดหวังให้ทุกคนปฏิบัติตาม
MarsAtomic

ฉันไม่เห็นด้วยโปรดเปรียบเทียบคำตอบของฉันกับคนอื่นโดยเฉพาะคำตอบที่ได้คะแนนสูงสุด
Praveen Kumar

"ทำไมฉันทำไม่ได้เหรอ?" คำตอบ: "เพราะคุณทำไม่ได้"
JacksOnF1re

0

วิธีการปกติสามารถเป็นนามธรรมได้เมื่อพวกเขาตั้งใจจะถูกแทนที่โดย subclasses และจัดให้มีฟังก์ชั่น ลองนึกภาพว่าคลาสFooนั้นขยายออกไปBar1, Bar2, Bar3ดังนั้นแต่ละคลาสจะมีคลาสนามธรรมของตัวเองตามความต้องการ

ตอนนี้วิธีการคงที่ตามคำนิยามอยู่ในชั้นเรียนพวกเขาไม่มีอะไรจะทำอย่างไรกับวัตถุของชั้นเรียนหรือวัตถุของชั้นย่อยของมัน พวกเขาไม่จำเป็นต้องมีอยู่พวกเขาสามารถใช้งานได้โดยไม่ต้องทำให้ชั้นเรียนเป็นอินสแตนซ์ ดังนั้นพวกเขาจำเป็นต้องพร้อมใช้งานและไม่สามารถพึ่งพาคลาสย่อยเพื่อเพิ่มฟังก์ชันการทำงานให้กับพวกเขา


0

เพราะนามธรรมเป็นคำหลักที่ใช้กับวิธีการนามธรรมไม่ได้ระบุเนื้อหา และถ้าเราพูดถึงคำหลักคงที่มันเป็นพื้นที่ชั้นเรียน


โปรดอธิบายรายละเอียดเล็กน้อยเกี่ยวกับคำตอบของคุณ
Blip

0

เพราะถ้าคุณใช้สมาชิกแบบสแตติกหรือตัวแปรแบบสแตติกในคลาสมันจะโหลด ณ เวลาที่โหลดคลาส


3
และทำไมถึงเป็นปัญหา?
eis

-1

คุณสามารถทำได้ด้วยส่วนต่อประสานใน Java 8

นี่คือเอกสารเกี่ยวกับมันอย่างเป็นทางการ:

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


2
อย่างไร? ฉันหาวิธีแก้ปัญหาแล้วหาไม่เจอ
yeliha

wut? คุณไม่สามารถ. วิธีการเชื่อมต่อคงที่ทั้งหมดจะต้องเรียกใช้โดยใช้อินเตอร์เฟซคลาส
mmm

8
คำตอบนี้ผิดและแนะนำให้ผู้คนใหม่ ๆ ใน java สิ่งนี้ใช้เพื่อแสดงตัวอย่างของวิธีการคงที่แบบนามธรรมเนื่องจากไม่สามารถใช้งานได้และระบุไว้ในคำตอบอื่น ๆ
Blip

-1

เพราะถ้าคลาสขยายคลาสนามธรรมแล้วมันจะต้องแทนที่เมธอด abstract และนั่นเป็นสิ่งที่จำเป็น และเนื่องจากวิธีการแบบคงที่เป็นวิธีการเรียนที่ได้รับการแก้ไขในเวลารวบรวมในขณะที่วิธีการแทนที่เป็นวิธีการอินสแตนซ์ได้รับการแก้ไขในขณะทำงานและต่อไปนี้ความแตกต่างแบบไดนามิก


โปรดใช้ประโยชน์และคั่นความยุ่งเหยิงนี้ให้เป็นประโยชน์
มาร์ควิสแห่ง Lorne
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.