java - उपच - पागलपन प्रकार




आदिम प्रकार के कारण कोड दोहराव: कैसे पागलपन से बचने के लिए? (6)

क्या जावा 7 में कोई बदलाव है जो ऐसे मामलों में तनाव को कम कर सकता है?

नहीं।

क्या इस मामले को संभालने के लिए कहीं एनोटेशन प्रोसेसर है?

ऐसा नहीं कि मुझे पता नहीं।

यदि नहीं, तो इसे बनाने में क्या लगेगा?

समय, या पैसा। :-)

यह मुझे एक समस्या-स्थान की तरह लगता है जहां सामान्य समाधान के साथ आना मुश्किल होगा जो अच्छी तरह से काम करता है ... तुच्छ मामलों से परे। पारंपरिक स्रोत कोड पीढ़ी या एक (पाठ) पूर्वप्रक्रमक मुझे और अधिक आशाजनक लगता है। (हालांकि मैं एनोटेशन प्रोसेसर विशेषज्ञ नहीं हूं।)

मेरे जावा प्रोजेक्ट में से एक में मैं जावा पुनरावृत्ति के कारण कोड की पुनरावृत्ति से त्रस्त हूं, जो कि आदिम (not) हैंडल (not) । तीसरी बार फिर से चार अलग-अलग स्थानों ( int , long , float , double ) में एक ही बदलाव को मैन्युअल रूप से कॉपी करने के बाद, फिर से मैं स्निपिंग के लिए वास्तव में करीब (?) आया।

विभिन्न रूपों में, इस मुद्दे को अब StackOverflow पर लाया गया है:

  • जावा में अत्यधिक दोहराव वाले कोड और प्रलेखन का प्रबंधन करना
  • आदिम प्रकारों के साथ काम करते समय पुनरावृत्ति से कैसे बचें?
  • एक जावा विधि के लिए प्राथमिकताओं की गतिशील सूची पास करना

सर्वसम्मति से दो संभावित विकल्पों में परिवर्तित किया गया:

  • किसी प्रकार के कोड जनरेटर का उपयोग करें।
  • तुम क्या कर सकते हो? C'est la vie!

खैर, दूसरा समाधान यह है कि मैं अब क्या कर रहा हूं और यह मेरी पवित्रता के लिए धीरे-धीरे खतरनाक हो रहा है, बहुत अच्छी तरह से ज्ञात यातना तकनीक की तरह

इन प्रश्नों को पूछे जाने में दो साल बीत चुके हैं और जावा 7 साथ आया है। इसलिए, मैं एक आसान और / या अधिक मानक समाधान के लिए आशान्वित हूं।

  • क्या जावा 7 में कोई बदलाव है जो ऐसे मामलों में तनाव को कम कर सकता है? मुझे संघनित परिवर्तन सारांश में कुछ भी नहीं मिला, लेकिन शायद कहीं कुछ अस्पष्ट नई सुविधा है?

  • जबकि स्रोत कोड पीढ़ी एक विकल्प है, मैं मानक JDK सुविधा सेट का उपयोग कर समर्थित समाधान पसंद करूंगा। निश्चित रूप से, cpp या किसी अन्य कोड जनरेटर का उपयोग करना काम करेगा, लेकिन यह अधिक निर्भरता जोड़ता है और निर्माण प्रणाली में बदलाव की आवश्यकता होती है।

    JDK द्वारा समर्थित प्रतीत होने वाले प्रकारों की एकमात्र कोड पीढ़ी प्रणाली एनोटेशन तंत्र के माध्यम से है। मैं एक प्रोसेसर की कल्पना करता हूं जो इस तरह स्रोत कोड का विस्तार करेगा:

    @Primitives({ "int", "long", "float", "double" })
    @PrimitiveVariable
    int max(@PrimitiveVariable int a, @PrimitiveVariable int b) {
        return (a > b)?a:b;
    }

    आदर्श आउटपुट फ़ाइल में इस पद्धति के चार अनुरोधित रूपांतर शामिल होंगे, अधिमानतः संबंधित Javadoc टिप्पणियों आदि के साथ। क्या इस मामले को संभालने के लिए कहीं एनोटेशन प्रोसेसर है? यदि नहीं, तो इसे बनाने में क्या लगेगा?

  • शायद कुछ अन्य चाल जो हाल ही में पॉप अप हुई हैं?

संपादित करें:

एक महत्वपूर्ण टिप्पणी: जब तक मेरे पास कोई कारण नहीं था, मैं आदिम प्रकारों का उपयोग नहीं करूंगा। अब भी कुछ अनुप्रयोगों में बॉक्सिंग प्रकारों के उपयोग से बहुत वास्तविक प्रदर्शन और मेमोरी प्रभाव होता है।

संपादित करें 2:

एक उदाहरण के रूप में max() का उपयोग करना, सभी संख्यात्मक बॉक्सिंग प्रकारों में उपलब्ध compareTo() विधि का उपयोग करने की अनुमति देता है। यह थोड़ा पेचीदा मामला है:

int sum(int a, int b) {
    return a + b;
}

वास्तव में छह या सात बार लिखे बिना सभी संख्यात्मक बॉक्सिंग प्रकारों के लिए इस पद्धति का समर्थन करने के बारे में कैसे जाना जा सकता है?


आपका प्रश्न बहुत विस्तृत है क्योंकि आप पहले से ही सभी 'अच्छे' उत्तरों को जानते हैं। चूंकि भाषा डिजाइन के कारण हमें प्राइमेटिक्स को जेनेरिक पैरामीटर प्रकारों के रूप में उपयोग करने की अनुमति नहीं है, इसलिए सबसे अच्छा व्यावहारिक उत्तर है जहां @PeterLawrey शीर्षक है।

public class PrimitiveGenerics {

    public static double genericMax( double a, double b) {
        return (a > b) ?a:b;
    }


    public int max( int a, int b) {
        return (int) genericMax(a, b);
    }
    public long max( long a, long b) {
        return (long) genericMax(a, b);
    }
    public float max( float a, float b) {
        return (float) genericMax(a, b);
    }
    public double max( double a, double b) {
        return (double) genericMax(a, b);
    }


}

भाषा के भविष्य के विकास में आदिम प्रकार की सूची छोटी और उम्मीद के मुताबिक स्थिर है और डबल प्रकार सबसे व्यापक / सबसे सामान्य है।

सबसे खराब स्थिति में, आप 64 बिट चर का उपयोग करके गणना करते हैं जहां 32 बिट पर्याप्त होगा। रूपांतरण (छोटे) के लिए और एक अधिक विधि (छोटे) में मान से पास होने के लिए एक प्रदर्शन दंड है, लेकिन कोई भी वस्तु नहीं बनाई जाती है क्योंकि यह आदिम आवरणों का उपयोग करने के लिए मुख्य (और वास्तव में बहुत बड़ा) दंड है।

मैंने एक स्थैतिक विधि का भी उपयोग किया है, इसलिए यह जल्दी से बाध्य है और रन-टाइम में नहीं है, हालांकि यह सिर्फ एक है और जेवीएम अनुकूलन आमतौर पर ध्यान रखता है, लेकिन यह वैसे भी चोट नहीं पहुंचाएगा। वास्तविक मामले के परिदृश्य पर निर्भर हो सकता है।

प्यारा होगा अगर किसी ने इसका परीक्षण किया, लेकिन मेरा मानना ​​है कि यह सबसे अच्छा समाधान है।

अद्यतन: @ थेकला की टिप्पणी के आधार पर, डबल केवल तब तक एस-का प्रतिनिधित्व कर सकता है जब तक कि कुछ परिमाण नहीं हो जाता क्योंकि यह सटीक खो देता है (लंबे एस के साथ निपटने के दौरान अपवित्र हो जाता है):

public class Asdf2 {

    public static void main(String[] args) {
        System.out.println(Double.MAX_VALUE); //1.7976931348623157E308
        System.out.println( Long.MAX_VALUE); //9223372036854775807
        System.out.println((double) Long.MAX_VALUE); //9.223372036854776E18
    }
}

प्रदर्शन के दृष्टिकोण से (मैं बहुत सीपीयू-बाउंड एल्गोरिदम भी बनाता हूं), मैं अपने स्वयं के बॉक्सिंग का उपयोग करता हूं जो अपरिवर्तनीय नहीं हैं। यह उच्च प्रदर्शन के साथ काम करने के लिए ArrayList और HashMap जैसे सेट में परस्पर संख्याओं का उपयोग करने की अनुमति देता है।

यह सभी आदिम कंटेनरों को उनके दोहराए गए कोड के साथ बनाने के लिए एक लंबा तैयारी कदम उठाता है, और फिर आप बस उनका उपयोग करते हैं। जैसा कि मैंने 2-आयामी, 3-आयामी आदि मूल्यों के साथ भी व्यवहार किया है, मैंने अपने लिए भी बनाया है। चुनना आपको है।

पसंद:
Vector1i 1 Vector1i - 1 पूर्णांक, पूर्णांक की जगह लेता है
Vector2i 2 Vector2i - 2 पूर्णांक, Point और Dimension को बदलता है
Vector2d 2 Vector2d - 2 डबल्स, Vector2d 2 Point2D.Double जगह। Point2D.Double
Vector4i 4 Vector4i - 4 पूर्णांक, Rectangle जगह ले सकता है
वेक्टर 2 Vector2f - 2-आयामी फ्लोट वेक्टर
वेक्टर 3 Vector3f - 3-आयामी फ्लोट वेक्टर
...आदि...
ये सभी गणित में एक सामान्यीकृत 'सदिश' का प्रतिनिधित्व करते हैं, इसलिए इन सभी आदिमों के लिए नाम।

एक नकारात्मक पक्ष यह है कि आप a+b नहीं कर सकते हैं, आपके पास a.add(b) जैसी विधियाँ हैं, और a=a+b मैंने a.addSelf(b) जैसी विधियों का नाम चुना है। यदि यह आपको परेशान करता है, तो Ceylon पर एक नज़र डालें, जिसे मैंने हाल ही में खोजा था। यह विशेष रूप से इसकी सीमाओं (जैसे ऑपरेटर ओवरलोडिंग) को संबोधित करने के लिए बनाई गई जावा (जेवीएम / ईक्लिसपी कॉम्पेटिबाइल) के ऊपर एक परत है।

एक अन्य बात, इन वर्गों को Map में एक कुंजी के रूप में उपयोग करते समय देखें, जब मान बदल जाता है तो छंटनी / हैशिंग / तुलना करना हैवीयर हो जाएगा।


मैं पिछले उत्तरों / टिप्पणियों से सहमत हूँ जो कहते हैं कि "JDK मानक सेट का उपयोग करके आप जो चाहते हैं वैसा करने का कोई तरीका नहीं है ।" इस प्रकार, आपको कुछ कोड जेनरेशन करने होंगे, हालांकि इसके लिए निर्माण प्रणाली में बदलाव की आवश्यकता नहीं होगी। चूंकि आप पूछते हैं:

... यदि नहीं, तो इसे बनाने में क्या लगेगा?

... एक साधारण मामले के लिए, बहुत ज्यादा नहीं, मुझे लगता है। मान लीजिए कि मैंने अपने आदिम संचालन को एक उपयोग वर्ग में रखा:

public class NumberUtils {

    // @PrimitiveMethodsStart
    /** Find maximum of int inputs */
    public static int max(int a, int b) {
        return (a > b) ? a : b;
    }

    /** Sum the int inputs */
    public static int sum(int a, int b) {
        return a + b;
    }
    // @PrimitiveMethodsEnd

    // @GeneratedPrimitiveMethodsStart - Do not edit below
    // @GeneratedPrimitiveMethodsEnd
}

तब मैं 30 से कम लाइनों में एक साधारण प्रोसेसर लिख सकता हूं:

public class PrimitiveMethodProcessor {
    private static final String PRIMITIVE_METHODS_START = "@PrimitiveMethodsStart";
    private static final String PRIMITIVE_METHODS_END = "@PrimitiveMethodsEnd";
    private static final String GENERATED_PRIMITIVE_METHODS_START = "@GeneratedPrimitiveMethodsStart";
    private static final String GENERATED_PRIMITIVE_METHODS_END = "@GeneratedPrimitiveMethodsEnd";

    public static void main(String[] args) throws Exception {
        String fileName = args[0];
        BufferedReader inputStream = new BufferedReader(new FileReader(fileName));
        PrintWriter outputStream = null;
        StringBuilder outputContents = new StringBuilder();
        StringBuilder methodsToCopy = new StringBuilder();
        boolean inPrimitiveMethodsSection = false; 
        boolean inGeneratedPrimitiveMethodsSection = false; 
        try {
            for (String line;(line = inputStream.readLine()) != null;) {
                if(line.contains(PRIMITIVE_METHODS_END)) inPrimitiveMethodsSection = false;
                if(inPrimitiveMethodsSection)methodsToCopy.append(line).append('\n');
                if(line.contains(PRIMITIVE_METHODS_START)) inPrimitiveMethodsSection = true;
                if(line.contains(GENERATED_PRIMITIVE_METHODS_END)) inGeneratedPrimitiveMethodsSection = false;
                if(!inGeneratedPrimitiveMethodsSection)outputContents.append(line).append('\n');
                if(line.contains(GENERATED_PRIMITIVE_METHODS_START)) {
                    inGeneratedPrimitiveMethodsSection = true;
                    String methods = methodsToCopy.toString();
                    for (String primative : new String[]{"long", "float", "double"}) {
                        outputContents.append(methods.replaceAll("int\\s", primative + " ")).append('\n');
                    }
                }
            }
            outputStream = new PrintWriter(new FileWriter(fileName));
            outputStream.print(outputContents.toString());
        } finally {
            inputStream.close();
            if(outputStream!= null) outputStream.close();
        }
    }
}

यह @PrimitiveMethods अनुभाग में विधियों के लंबे, फ्लोट और दोहरे संस्करणों के साथ @GeneratedPrimitiveMethods अनुभाग को भर देगा।

    // @GeneratedPrimitiveMethodsStart - Do not edit below
    /** Find maximum of long inputs */
    public static long max(long a, long b) {
        return (a > b) ? a : b;
    }
    ...

यह जानबूझकर एक सरल उदाहरण है, और मुझे यकीन है कि यह सभी मामलों को कवर नहीं करता है, लेकिन आप बिंदु प्राप्त करते हैं और यह देख सकते हैं कि इसे कैसे बढ़ाया जा सकता है जैसे कि एकाधिक फ़ाइलों की खोज करना या सामान्य एनोटेशन का उपयोग करना और विधि समाप्त होने का पता लगाना।

इसके अलावा, जब तक आप इसे अपनी निर्माण प्रणाली में एक कदम के रूप में स्थापित कर सकते हैं, मैंने इसे अपने ग्रहण परियोजना में जावा बिल्डर से पहले एक बिल्डर के रूप में चलाने के लिए सेट किया। अब जब भी मैं फ़ाइल को संपादित करता हूं और बचत करता हूं; यह अपने आप अपडेट हो जाता है, जगह में, एक चौथाई से भी कम समय में। इस प्रकार, यह एक संपादन उपकरण के अधिक हो जाता है, निर्माण प्रणाली में एक कदम से।

सिर्फ एक विचार...


यदि मैं अभी भी एक आदिम चाहता हूं तो मैं "सुपर प्रकार" का उपयोग करना पसंद करता हूं। प्रदर्शन आमतौर पर बहुत करीब होता है और यह बहुत सारी विविधताएं बनाने से बचता है। BTW: 64-बिट JVM में रजिस्टर सभी 64-बिट वैसे भी होगा।


हे। डरपोक क्यों नहीं? प्रतिबिंब के साथ, आप एक विधि के लिए एनोटेशन खींच सकते हैं (आपके द्वारा पोस्ट किए गए उदाहरण के समान एनोटेशन)। फिर आप सदस्य नामों को प्राप्त करने के लिए प्रतिबिंब का उपयोग कर सकते हैं, और उपयुक्त प्रकारों में डाल सकते हैं ... एक system.out.println स्टेटमेंट में।

आप इसे एक बार चलाएंगे, या हर बार जब आप कक्षा को मॉडरेट करेंगे। तब आउटपुट को कॉपी-पेस्ट किया जा सकता था। यह संभवतः आपको महत्वपूर्ण समय बचाएगा, और विकसित करने के लिए बहुत मुश्किल नहीं होगा।

एचएम, विधियों की सामग्री के लिए के रूप में ... मेरा मतलब है, यदि आपके सभी तरीके तुच्छ हैं, तो आप शैली को हार्ड कोड कर सकते हैं (अर्थात यदि मेथडनाम। असेंबल ("अधिकतम") प्रिंट ए> बी: ए: बी आदि। जहां मेथडनाम को प्रतिबिंब के माध्यम से निर्धारित किया जाता है), या आप कर सकते हैं, उम्म ... एचएम। मैं कल्पना कर रहा हूं कि सामग्री को आसानी से चिपकाया जा सकता है, लेकिन यह सिर्फ अधिक काम लगता है।

ओह! जो "सामग्री" नामक एक और एनोटेशन नहीं बनाते हैं, इसे विधि सामग्री का एक स्ट्रिंग मान दें, इसे सदस्य में जोड़ें, और अब आप सामग्री को प्रिंट भी कर सकते हैं।

बहुत कम समय में, इस सहायक को कोड करने में समय व्यतीत हो गया, भले ही जब तक थकाऊ काम करने के बारे में, ठीक है, यह अधिक दिलचस्प, riiiight होगा?





code-duplication