2009-12-19 8 views
36

मैं समझता हूँ कि इस कोड में:क्या अंतिम स्थिर विधि घोषित करना एक बुरा विचार है?

class Foo { 
    public static void method() { 
     System.out.println("in Foo"); 
    } 
} 

class Bar extends Foo { 
    public static void method() { 
     System.out.println("in Bar"); 
    } 
} 

.. Bar 'खाल' में स्थिर विधि Foo में घोषित, के रूप में विरोध किया स्थिर विधि यह बहुरूपता अर्थ में अधिभावी करने के लिए।

class Test { 
    public static void main(String[] args) { 
     Foo.method(); 
     Bar.method(); 
    } 
} 

... इच्छा उत्पादन:

in Foo
in Bar

पुन: परिभाषित करने Foo में final रूप method() इसे छिपाने के लिए Bar की क्षमता को अक्षम होगा। जब आप विधि को final के रूप में चिह्नित करते हैं तो संकलन विफल हो जाता है।

क्या यह final के रूप में स्थैतिक तरीकों की घोषणा करने के लिए खराब अभ्यास माना जाता है, अगर यह जानबूझकर या अनजाने में विधि को दोबारा परिभाषित करने से उप-वर्गों को रोकता है?

(this क्या final का उपयोग करने का व्यवहार है का एक अच्छा विवरण है ..)

+3

आपको यह कहां मिला बुरा अभ्यास? –

+1

प्रश्न को फिर से लिखा;) डिफ़ॉल्ट इंटेलिजे निरीक्षण से पता चलता है कि यह है, और मुझे Google खोजों से एक निर्णायक उत्तर नहीं मिला था। – brasskazoo

+1

नेटबीन के निरीक्षण से यह भी पता चलता है कि यह खराब अभ्यास है। – steve

उत्तर

34

मैं विचार नहीं करते यह final के रूप में एक static विधि चिह्नित करने के लिए बुरा व्यवहार है।

जैसा कि आपने पाया, final उपclasses द्वारा छिपाने से विधि को रोक देगा जो बहुत अच्छी खबर imho है।

मैं काफी अपने बयान से हैरान हूँ:

Re-defining method() as final in Foo will disable the ability for Bar to hide it, and re-running main() will output:

in Foo
in Foo

नहीं है, Foo में final के रूप में विधि अंकन संकलन से Bar कर पाएगा। ग्रहण में कम से कम मैं हो रही है:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: Cannot override the final method from Foo

इसके अलावा, मुझे लगता है कि लोगों को हमेशा भी वर्ग के भीतर ही वर्ग के नाम के साथ उन्हें योग्यता static विधि आह्वान करना चाहिए:

class Foo 
{ 
    private static final void foo() 
    { 
    System.out.println("hollywood!"); 
    } 

    public Foo() 
    { 
    foo();  // both compile 
    Foo.foo(); // but I prefer this one 
    } 
} 
+0

हां, हमें सभी सार्वजनिक स्थैतिक विधियों को 'अंतिम' के रूप में चिह्नित करने की आदत बनाना चाहिए। – ZhongYu

3

आमतौर पर उपयोगिता वर्गों के साथ - केवल स्थिर तरीकों वाले वर्ग - यह विरासत का उपयोग करने के लिए अवांछनीय है। इस कारण से आप वर्ग को अन्य वर्गों को विस्तारित करने के लिए अंतिम रूप में परिभाषित करना चाह सकते हैं। यह आपके उपयोगिता वर्ग विधियों पर अंतिम संशोधक डालने से इनकार कर देगा।

+0

अच्छा बिंदु - मेरे रिफैक्टरिंग लक्ष्यों में से एक स्थिर विधियों को एक उपयोगिता वर्ग में स्थानांतरित करना है, जो कि कक्षा को अंतिम रूप में चिह्नित करना एक अच्छा विचार है। – brasskazoo

1

स्थैतिक तरीकों को अंतिम रूप में चिह्नित करना एक अच्छी बात हो सकती है, खासकर यदि आप ऐसे ढांचे को विकसित कर रहे हैं जिन्हें आप दूसरों की विस्तार करने की उम्मीद करते हैं। इस तरह आपके उपयोगकर्ता अनजाने में अपनी कक्षाओं में अपनी स्थिर विधियों को छिपाने में अंत नहीं होंगे। लेकिन यदि आप एक ढांचा विकसित कर रहे हैं तो आप शुरू करने के लिए स्थिर तरीकों का उपयोग करने से बच सकते हैं।

3

कोड संकलन नहीं करता है:

Test.java:8: method() in Bar cannot override method() in Foo; overridden method is static final public static void method() {

संदेश भ्रामक है के बाद से एक स्थिर विधि परिभाषा के अनुसार अधिरोहित जा कभी नहीं कर सकते हैं।

मैं जब (नहीं 100% हर समय कोडिंग निम्न कार्य करें, लेकिन कुछ भी यहाँ "गलत" है:

("नियम" का पहला सेट सबसे चीजों के लिए किया जाता है - कुछ विशेष मामलों के बाद कवर कर रहे हैं)

  1. एक अंतरफलक
  2. एक अमूर्त वर्ग है कि इंटरफ़ेस को लागू करता है
  3. ठोस वर्ग कि अमूर्त वर्ग का विस्तार
  4. concret बनाते बनाते हैं ई वर्गों है कि इंटरफ़ेस को लागू करता है लेकिन,,
  5. हमेशा अमूर्त वर्ग का विस्तार नहीं करता है, तो संभव बनाने के सभी चर/स्थिरांक/इंटरफ़ेस

के मापदंडों एक अंतरफलक के बाद से स्थिर तरीकों आप हवा नहीं है नहीं हो सकता इस मुद्दे के साथ। यदि आप अमूर्त वर्ग या ठोस कक्षाओं में स्थिर तरीके बनाने जा रहे हैं तो उन्हें निजी होना चाहिए, फिर उन्हें ओवरराइड करने का कोई तरीका नहीं है।

विशेष मामलों:

उपयोगिता वर्गों (वर्गों सभी स्थैतिक तरीकों के साथ):

  1. के रूप में अंतिम
  2. वर्ग घोषित यह को रोकने के लिए एक निजी निर्माता देने के आकस्मिक निर्माण

यदि आप एक ठोस या अमूर्त वर्ग में एक स्थिर विधि चाहते हैं जो निजी नहीं है तो आप इसके बजाय इसके बजाय उपयोगिता वर्ग बनाना चाहते हैं।

मूल्य वर्गों (एक वर्ग बहुत विशेष है कि अनिवार्य रूप से डेटा रखने के लिए, java.awt.Point की तरह है, जहां यह काफी रखा है x और y मान):

  1. एक इंटरफेस बनाने की आवश्यकता
  2. कोई सार वर्ग बनाने की कोई आवश्यकता नहीं
  3. कक्षा अंतिम
  4. गैर-निजी स्थैतिक विधियां ठीक हैं, खासकर निर्माण के लिए क्योंकि आप कैशिंग करना चाहते हैं।

यदि आप उपर्युक्त सलाह का पालन करते हैं तो आप बहुत लचीले कोड के साथ हवादार होंगे जिसमें जिम्मेदारियों का काफी साफ अलगाव भी होगा।

import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 


public final class Location 
    implements Comparable<Location> 
{ 
    // should really use weak references here to help out with garbage collection 
    private static final Map<Integer, Map<Integer, Location>> locations; 

    private final int row;  
    private final int col; 

    static 
    { 
     locations = new HashMap<Integer, Map<Integer, Location>>(); 
    } 

    private Location(final int r, 
        final int c) 
    { 
     if(r < 0) 
     { 
      throw new IllegalArgumentException("r must be >= 0, was: " + r); 
     } 

     if(c < 0) 
     { 
      throw new IllegalArgumentException("c must be >= 0, was: " + c); 
     } 

     row = r; 
     col = c; 
    } 

    public int getRow() 
    { 
     return (row); 
    } 

    public int getCol() 
    { 
     return (col); 
    } 

    // this ensures that only one location is created for each row/col pair... could not 
    // do that if the constructor was not private. 
    public static Location fromRowCol(final int row, 
             final int col) 
    { 
     Location    location; 
     Map<Integer, Location> forRow; 

     if(row < 0) 
     { 
      throw new IllegalArgumentException("row must be >= 0, was: " + row); 
     } 

     if(col < 0) 
     { 
      throw new IllegalArgumentException("col must be >= 0, was: " + col); 
     } 

     forRow = locations.get(row); 

     if(forRow == null) 
     { 
      forRow = new HashMap<Integer, Location>(col); 
      locations.put(row, forRow); 
     } 

     location = forRow.get(col); 

     if(location == null) 
     { 
      location = new Location(row, col); 
      forRow.put(col, location); 
     } 

     return (location); 
    } 

    private static void ensureCapacity(final List<?> list, 
             final int  size) 
    { 
     while(list.size() <= size) 
     { 
      list.add(null); 
     } 
    } 

    @Override 
    public int hashCode() 
    { 
     // should think up a better way to do this... 
     return (row * col); 
    } 

    @Override 
    public boolean equals(final Object obj) 
    { 
     final Location other; 

     if(obj == null) 
     { 
      return false; 
     } 

     if(getClass() != obj.getClass()) 
     { 
      return false; 
     } 

     other = (Location)obj; 

     if(row != other.row) 
     { 
      return false; 
     } 

     if(col != other.col) 
     { 
      return false; 
     } 

     return true; 
    } 

    @Override 
    public String toString() 
    { 
     return ("[" + row + ", " + col + "]"); 
    } 

    public int compareTo(final Location other) 
    { 
     final int val; 

     if(row == other.row) 
     { 
      val = col - other.col; 
     } 
     else 
     { 
      val = row - other.row; 
     } 

     return (val); 
    } 
} 
0

मैं वसंत के AOP और MVC का उपयोग कर अंतिम तरीकों का उपयोग कर के लिए एक हानि का सामना करना पड़ा:

एक उदाहरण मान वर्ग इस स्थान वर्ग है। मैं सारफॉर्म कंट्रोलर में से किसी एक तरीके के आसपास सुरक्षा हुक में वसंत के एओपी को इस्तेमाल करने की कोशिश कर रहा था जिसे अंतिम घोषित किया गया था। मुझे लगता है कि वसंत कक्षाओं में इंजेक्शन के लिए बीएसएल लाइब्रेरी का उपयोग कर रहा था और वहां कुछ सीमा थी।

0

जब मैं शुद्ध उपयोगिता कक्षाएं बनाता हूं, तो मैं एक निजी निर्माता के साथ घोषणा करता हूं ताकि उन्हें विस्तारित नहीं किया जा सके।सामान्य कक्षाएं बनाते समय, मैं अपने तरीकों को स्थैतिक घोषित करता हूं यदि वे किसी भी वर्ग आवृत्ति चर का उपयोग नहीं कर रहे हैं (या, कुछ मामलों में, भले ही वे थे, मैं विधि में तर्क पारित कर दूंगा और इसे स्थैतिक बना दूंगा, यह देखना आसान है विधि क्या कर रही है)। इन विधियों को स्थिर घोषित किया गया है लेकिन वे भी निजी हैं - वे केवल कोड डुप्लिकेशन से बचने के लिए हैं या कोड को समझने में आसान बनाने के लिए हैं।

कहा जा रहा है कि, मुझे उस मामले में चलना याद नहीं है जहां आपके पास ऐसी कक्षा है जिसमें सार्वजनिक स्थैतिक विधियां हैं और जिन्हें बढ़ाया जा सकता है। लेकिन, यहां जो बताया गया था, उसके आधार पर, मैं अपनी स्थिर विधियों को अंतिम घोषित कर दूंगा।

5

अगर मैं एक public static विधि है, तो यह अक्सर पहले से ही एक तथाकथित उपयोगिता वर्ग में केवल static तरीकों के साथ स्थित है। आत्म-व्याख्या करने वाले उदाहरण StringUtil, SqlUtil, IOUtil, आदिटर हैं। उन उपयोगिता वर्गों ने पहले से ही final घोषित कर दिया है और private कन्स्ट्रक्टर के साथ आपूर्ति की है। जैसे

public final class SomeUtil { 

    private SomeUtil() { 
     // Hide c'tor. 
    } 

    public static SomeObject doSomething(SomeObject argument1) { 
     // ... 
    } 

    public static SomeObject doSomethingElse(SomeObject argument1) { 
     // ... 
    } 

} 

इस तरह आप उन्हें ओवरराइड नहीं कर सकते हैं।

यदि आपका उपयोगिता वर्ग की तरह नहीं है, तो मैं public संशोधक के मान पर सवाल उठाउंगा। यह private नहीं होना चाहिए? अन्यथा इसे कुछ उपयोगिता वर्ग में ले जाएं। public static विधियों के साथ "सामान्य" वर्गों को अव्यवस्थित न करें। इस तरह आपको उन्हें final चिह्नित करने की आवश्यकता नहीं है।

एक और मामला एक प्रकार का सार कारखाना वर्ग है, जो public static विधि के माध्यम से स्वयं के ठोस कार्यान्वयन देता है। इस तरह के मामले में यह final विधि को चिह्नित करने के लिए पूरी तरह से समझ में आता है, आप नहीं चाहते कि कंक्रीट कार्यान्वयन विधि को ओवरराइड करने में सक्षम हो।

+0

मुझे लगता है कि इसे व्यक्तिगत रूप से सही उत्तर के रूप में चिह्नित किया जाना चाहिए। यह वही है जो मैंने सुझाया होगा। कक्षा को अंतिम रूप दें, व्यक्तिगत विधि नहीं। –

31

स्टेटिक विधियां जावा की सबसे भ्रमित सुविधाओं में से एक हैं। इसे ठीक करने के लिए सर्वोत्तम अभ्यास हैं, और सभी स्थिर तरीकों को final इन सर्वोत्तम प्रथाओं में से एक है!

स्थिर तरीकों के साथ समस्या यह है कि

  • वे कक्षा तरीकों नहीं हैं, लेकिन वैश्विक कार्यों एक classname के साथ उपसर्ग
  • यह अजीब बात है कि वे उपवर्गों "प्राप्त" कर रहे हैं
  • यह आश्चर्य की बात है वे ओवरराइड नहीं कर सकते हैं कि हो लेकिन छिपा
  • यह पूरी तरह से टूट गया है कि वे रिसीवर के रूप में एक उदाहरण के साथ कहा जा सकता है

इसलिए आपको चाहिए

  • हमेशा रिसीवर के रूप में अपनी कक्षा से उन्हें फोन
  • हमेशा ही रिसीवर के रूप में घोषित करने वर्ग के साथ उन्हें फोन
  • हमेशा उन्हें (या की घोषणा वर्ग) बनाने final

और आपको

  • कभी नहीं होना चाहिए : रिसीवर के रूप में एक उदाहरण के साथ उन्हें फोन
  • कभी नहीं रिसीवर के रूप में उनकी घोषित वर्ग का एक उपवर्ग के साथ उन्हें फोन
  • उपवर्गों

 

एनबी में उन्हें फिर से परिभाषित कभी नहीं आपके प्रोग्राम का दूसरा संस्करण संकलन त्रुटि में विफल होना चाहिए। मुझे लगता है कि आपका आईडीई इस तथ्य को छिपा रहा है!

+0

बहुत अच्छा जवाब! वैसे, दूसरा संस्करण वास्तव में मेरे आईडीई में संकलन विफल रहता है। इसे स्पष्ट करने के लिए प्रश्न संपादित किया। – brasskazoo

+0

क्या आप बिल्कुल इस बारे में निश्चित हैं: हमेशा उन्हें (या घोषित कक्षा) अंतिम बनाएं - यदि आप कक्षा अंतिम घोषित करते हैं, तो स्वचालित रूप से सभी सार्वजनिक विधियां अंतिम होंगी, लेकिन क्या यह स्टेटिक विधियों के लिए भी लागू होती है? –

+0

यदि कक्षा अंतिम है तो यह उप-वर्ग नहीं हो सकता है, इसलिए यह सभी विधियों के साथ-साथ अंतिम भी है। स्थैतिक विधि के लिए इसका मतलब है कि उप-वर्ग विधि नहीं हो सकती है जो उन्हें छुपाती है, जो विधि को अंतिम बनाने के बराबर है लेकिन वर्ग नहीं है। – akuhn

1

इनमें से अधिकतर final समस्या उस समय तक की तारीख है जब वीएम-एस काफी गूंगा/रूढ़िवादी थे। इसके बाद यदि आपने एक विधि final चिह्नित की है तो इसका मतलब है (अन्य चीजों के साथ), कि वीएम विधि कॉल से परहेज कर सकता है। यह लंबे समय तक (या लंबे समय तक डबल: पी) समय के मामले में नहीं है: http://java.sun.com/developer/technicalArticles/Networking/HotSpot/inlining.html

मैं , लगता है कि आइडिया/Netbeans निरीक्षण में चेतावनी देता है क्योंकि यह सोचता है कि आप अनुकूलन के लिए final कीवर्ड का उपयोग करना चाहते हैं और उन्हें लगता है कि आप इस तथ्य है कि यह आधुनिक VMs साथ अनावश्यक है से अनजान हैं।

बस मेरे दो सेंट ...

0

क्योंकि स्थिर तरीकों वर्ग की संपत्ति हैं और वे नहीं बल्कि वस्तु की तुलना में वर्ग के नाम के साथ कहा जाता है। यदि हम पैरेंट क्लास विधि को अंतिम रूप में भी बनाते हैं तो इसे ओवरलोड नहीं किया जाएगा क्योंकि अंतिम विधियां इसकी मेमोरी लोकेशन को बदलने की अनुमति नहीं देती हैं, लेकिन हम उसी डेटा स्थान पर अंतिम डेटा सदस्य अपडेट कर सकते हैं ...