2009-10-08 6 views
12

निम्न दो जावा वर्गों पर विचार करें:अक्षम संकलन समय निर्भरता जब जावा वर्गों के संकलन की जाँच

a.) class Test { void foo(Object foobar) { } } 

b.) class Test { void foo(pkg.not.in.classpath.FooBar foobar) { } } 

इसके अलावा, मान लेते हैं कि pkg.not.in.classpath.FooBar classpath में नहीं मिला है।

प्रथम श्रेणी मानक जावैक का उपयोग करके ठीक संकलित करेगी।

हालांकि, दूसरी कक्षा संकलित नहीं होगी और जावैक आपको त्रुटि संदेश "package pkg.not.in.classpath does not exist" देगा।

त्रुटि संदेश आपके निर्भरता की जाँच के बाद सामान्य स्थिति में अच्छा है संकलक में बताने के लिए अगर आप कुछ विधि तर्क गलत, आदि

मिला जबकि अच्छी और उपयोगी संकलन समय पर निर्भरता के इस जाँच की अनुमति देता है AFAIK सख्ती से उपरोक्त उदाहरण में जावा क्लास फ़ाइल उत्पन्न करने के लिए आवश्यक नहीं है।

  1. आप किसी भी उदाहरण है जिसके लिए यह तकनीकी रूप से संकलन समय निर्भरता की जाँच के बिना प्रदर्शन एक वैध जावा वर्ग फ़ाइल उत्पन्न करने के लिए असंभव हो जाएगा दे सकते हैं?

  2. क्या आप संकलन समय निर्भरता जांच को छोड़ने के लिए जावैक या किसी अन्य जावा कंपाइलर को निर्देश देने के किसी भी तरीके से जानते हैं?

कृपया सुनिश्चित करें कि आपका उत्तर दोनों प्रश्नों को संबोधित करता है।

+0

एकमात्र (प्रदर्शनकर्ता) वर्कअराउंड एक 'मेथडहैंडल' को देखने के लिए है, इसे 'स्थिर अंतिम' फ़ील्ड में संग्रहीत करें और एमएच पर सही प्रकार के संकेतों के साथ 'invokeExact() 'का उपयोग करें।यदि आप इसे कुछ तरीकों से अधिक के लिए चाहते हैं तो यह काफी कठिन है। – the8472

उत्तर

19

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

इस कोड पर विचार करें: लक्ष्य विधि हस्ताक्षर public static void foo(int n) है

public class GotDeps { 
    public static void main(String[] args) { 
    int i = 1; 
    Dep.foo(i); 
    } 
} 

, तो इन निर्देशों का उत्पन्न हो जाएगा: लक्ष्य विधि हस्ताक्षर public static void foo(long n), तो है

public static void main(java.lang.String[]); 
    Code: 
    0: iconst_1 
    1: istore_1 
    2: iload_1 
    3: invokestatic #16; //Method Dep.foo:(I)V 
    6: return 

हैं int विधि आमंत्रण से पहले long पर प्रचारित किया जाएगा:

public static void main(java.lang.String[]); 
    Code: 
    0: iconst_1 
    1: istore_1 
    2: iload_1 
    3: i2l 
    4: invokestatic #16; //Method Dep.foo:(J)V 
    7: return 

इस मामले में, यह मंगलाचरण निर्देश या कैसे संख्या 16 से CONSTANT_Methodref_info संरचना लगातार पूल वर्ग में निर्दिष्ट पॉप्युलेट करने के लिए अधिक जानकारी के लिए वी एम कल्पना में class file format देखें उत्पन्न करने के लिए संभव नहीं होगा।

+0

महान जवाब! बिंदु और स्पष्ट प्रमाण के साथ (आमतौर पर एसओ :-) पर हाथ से चलने वाले हाथ से चलने के विपरीत)। – knorv

+2

@knorv: क्या आप साबित कर सकते हैं कि एसओ पर हाथ से चल रहा है या क्या आप सिर्फ हाथ से चल रहे हैं? ;-) –

+0

जोआचिम: नहीं, यह बहुत ही व्यक्तिपरक है और इसलिए साबित करना असंभव है .-) – knorv

2

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

class test { 
    void foo (pkg.not.in.classpath.FooBar foobar) { 
     foobar.foobarMethod(); //what does the compiler do here? 
    } 
} 

आप कुछ स्थिति है जहाँ आप संकलित करने के लिए मिल गया है में शामिल हैं (और एक विधि कॉल) कुछ है कि एक पुस्तकालय पर काम करता है पर आप निकटतम करने के लिए पहुँच नहीं है आप आ सकता है, की तरह कुछ (विधि स्मृति से कहता है, गलत हो सकता है)

void foo(Object suspectedFoobar) 
    { 
     try{ 
     Method m = suspectedFoobar.getClass().getMethod("foobarMethod"); 
     m.invoke(suspectedFoobar); 
     } 
     ... 
    } 

मैं वास्तव में ऐसा करने का बिंदु, नहीं देख सकते हैं, हालांकि प्रतिबिंब के माध्यम से विधि प्राप्त करने के लिए है। क्या आप उस समस्या पर अधिक जानकारी प्रदान कर सकते हैं जिसे आप हल करने का प्रयास कर रहे हैं?

+0

कंपाइलर खुशी से कक्षा फ़ाइल उत्पन्न करेगा। अगर foobarMethod() मौजूद है या नहीं, तो रनटाइम चिंता है। – knorv

+0

"क्या आप उस समस्या पर अधिक जानकारी प्रदान कर सकते हैं जिसे आप हल करने का प्रयास कर रहे हैं?" मैं किसी भी समस्या को हल करने की कोशिश नहीं कर रहा हूं। JVM को बेहतर ढंग से समझने के लिए मैं अपने दो प्रश्नों के उत्तर जानना चाहता हूं। – knorv

+0

@knorv - "अगर foobarMethod() मौजूद है या नहीं, तो रनटाइम चिंता है।" - नहीं यह नहीं। जावा में कम से कम नहीं। – Nate

5

मुझे नहीं लगता कि ऐसा तरीका है - उचित बाइटकोड बनाने के लिए संकलक को तर्क के वर्ग के बारे में जानना आवश्यक है। यदि यह Foobar क्लास का पता नहीं लगा सकता है, तो यह Test कक्षा संकलित नहीं कर सकता है।

ध्यान दें कि आपके दो वर्ग कार्यात्मक रूप से समकक्ष हैं क्योंकि आप वास्तव में तर्क का उपयोग नहीं कर रहे हैं, वे समान नहीं हैं और संकलित होने पर विभिन्न बाइटकोड उत्पन्न करेंगे।

तो आपका आधार - कि संकलक को इस मामले में संकलन करने के लिए कक्षा को खोजने की आवश्यकता नहीं है - गलत है।

संपादित - अपनी टिप्पणी "पूछ करने के लिए संकलक सिर्फ तथ्य को नजरअंदाज नहीं कर सकते हैं और बाईटकोड कि वैसे भी उपयुक्त होगा उत्पन्न लगता है?। "

जवाब है कि नहीं है -। यह नहीं According to the Java Language Specification, विधि हस्ताक्षर प्रकार हैं, जिनमें elsewhere defined हैं रखना चाहिए कर सकते हैं संकलन समय पर समाधान योग्य होने के लिए

जिसका मतलब है कि जब यह यंत्रवत् काफी आसान होगा एक कंपाइलर बनाएं जो आप जो मांग रहे हैं वह करेगा, यह जेएलएस का उल्लंघन करेगा और इस प्रकार तकनीकी रूप से जावा कंपाइलर नहीं होगा। इसके अलावा, संकलन-समय सुरक्षा को बाधित करने से मुझे एक बड़ी बिक्री-बिंदु की तरह नहीं लगता है .. । :-)

+0

जेनरेट किया गया बाइटकोड "जावा/लैंग/ऑब्जेक्ट" से अधिक "पीकेजी/नहीं/इन/क्लासपाथ/फूबार" के साथ प्रतिस्थापित किया जा रहा है? कृपया और स्पष्ट बताएं। – knorv

+0

मुझे लगता है कि नमूना कोड एक विशेष मामला है क्योंकि ऑब्जेक्ट वास्तव में उपयोग नहीं किया जाता है (कोई विधि नहीं कहा जाता है, इसे कहीं भी तर्क के रूप में उपयोग नहीं किया जाता है), इसलिए इस मामले में एक कंपाइलर * इसके बारे में जानने के बिना संकलित करने में सक्षम हो सकता है कक्षा। लेकिन मैं इस विशेष मामले के लिए एक अच्छा उपयोग नहीं सोच सकता। –

+0

क्या आप मुझे यह सरल कंपाइलर दे सकते हैं जो तकनीकी रूप से जावा कंपाइलर नहीं है? मैं 'जावैक' खुश करने की कोशिश कर रहा हूं और सॉफ़्टवेयर विकास में शामिल अन्य सभी कार्यों को करने में पर्याप्त समय नहीं लगाता हूं। – ArtOfWarfare

0

निकालें इंटरफ़ेस

pkg.in.classpath.IFooBar 

FooBar implements IFooBar और

class Test { void foo(pkg.in.classpath.IFooBar foobar) {} } 

आपका टेस्ट वर्ग संकलित हो जाएगी बनाते हैं। कारखानों और विन्यास का उपयोग कर रनटाइम में सही कार्यान्वयन यानी FooBar प्लग करें। कुछ आईओसी कंटेनर देखें।

+0

मूल FooBar के संदर्भ में Foobar IFooBar को कैसे कार्यान्वित कर सकता है? विधि हस्ताक्षर समान है, भले ही संकलक इसे स्वीकार नहीं करेगा। –

+0

बोरिस: धन्यवाद, लेकिन मैं एक काम के आसपास की तलाश नहीं कर रहा हूँ। पूछे गए दो प्रश्नों के बारे में आप क्या सोचते हैं? – knorv

0

केवल एक चीज के बारे में आप कुछ बाइटकोड मैनिपुलेशन का उपयोग इसे अधिक विशिष्ट प्रकार में बदलने के लिए कर सकते हैं।

package pkg.not.in.classpath; 
public class FooBar { } 
इस से

:

package pkg.not.in.classpath; 
class FooBar { } 

तो वहाँ केवल अपने शब्द है कि यह वहाँ FooBar उपयोग करने के लिए कानूनी है

pkg.not.in.classpath.FooBar के उपयोग इस भेद करने के लिए जावा व्याकरण में कुछ भी नहीं है।

वहाँ

भी पैकेज के बीच एक अस्पष्टता स्रोत में वर्गों और इनर क्लासों scoped:

class pkg { 
    static class not { 
     static class in { 
      static class classpath { 
       static class FooBar {} 
      } 
     } 
    } 
} 

भीतरी वर्ग भी स्रोत में pkg.not.in.classpath.FooBar कहा जाता है लेकिन वर्ग फ़ाइल में के रूप में pkg$not$in$classpath$FooBar बजाय pkg/not/in/classpath/FooBar करने के लिए भेजा दिया जाएगा। ऐसा कोई रास्ता नहीं है कि जावैक क्लासपाथ में इसकी तलाश किए बिना इसका मतलब बता सके।

+0

आप बिल्कुल सही कक्षाएं हैं। लेकिन "सार्वजनिक" बनाम "संरक्षित" के संबंध में - जो बाइटकोड को परिवर्तित नहीं करता है और इसलिए सैद्धांतिक रूप से रनटाइम पर जांचने के लिए स्थगित कर दिया जा सकता है। या क्या मैं कुछ न कुछ भूल रहा हूं? – knorv

+0

हां, इसे बाद में स्थगित कर दिया जा सकता है - आईआईआरसी यह विफल हो जाएगा जब वर्ग को लोड करने पर सत्यापित किया गया था। –

1

डिज़ाइन द्वारा जावा संकलन-समय की डिस्पेंसी जांच करता है और इसे न केवल प्रकार निर्धारित करने के लिए उपयोग करता है बल्कि जब वे अधिभारित होते हैं तो विधि कॉल निर्धारित करने के लिए इसका उपयोग करता है। मुझे इसके चारों ओर कोई रास्ता नहीं पता है।

क्या किया जा सकता है (और, जेडीबीसी ड्राइवरों के लिए किया जाता है) प्रतिबिंब के उपयोग के माध्यम से निर्भरता की जांच में देरी है। संकलन समय पर कक्षा को जानने वाले संकलक के बिना आप Class.forName से कक्षा प्राप्त कर सकते हैं। आम तौर पर, इसका मतलब है कि कोड एक इंटरफ़ेस पर लिखा गया है और रनटाइम पर एक वर्ग लोड किया गया है जो इंटरफ़ेस लागू करता है।

2

यह वर्गों के प्रकार हस्ताक्षरों को देखे बिना कक्षा को संकलित करने के लिए जेएलएस का उल्लंघन होगा, जो इस पर निर्भर करता है। कोई अनुरूप जावा कंपाइलर आपको ऐसा करने की अनुमति नहीं देगा।

हालांकि ... कुछ ऐसा ही करना संभव है।

  1. संकलित A.java
  2. संकलित A.class के खिलाफ B.java: विशेष रूप से, अगर हम एक वर्ग ए और बी एक वर्ग है कि एक पर निर्भर करता है है, यह संभव निम्न करने के लिए है।
  3. एजेवा को एक असंगत तरीके से बदलने के लिए संपादित करें।
  4. पुरानी एक्लास की जगह एजेवा संकलित करें।
  5. बीक्लास और नया (असंगत) एक्लास का उपयोग कर जावा एप्लिकेशन चलाएं।

आप ऐसा करते हैं तो आवेदन जब वर्ग लोडर हस्ताक्षर असंगति देखती है एक IncompatibleClassChangeError साथ असफल हो जायेगी।

असल में, यह दिखाता है कि क्यों निर्भरता को अनदेखा करना एक बुरा विचार होगा। यदि आप असंगत बाइटकोड फ़ाइलों के साथ एक अनुप्रयोग चलाते हैं, (केवल) पता चला कि पहली असंगतता की सूचना दी जाएगी। तो यदि आपके पास बहुत सारी असंगतताएं हैं, तो आपको अपने आवेदन को कई बार "पहचानने" के लिए चलाने की आवश्यकता होगी। दरअसल, यदि आवेदन या किसी भी निर्भरता में कक्षाओं की गतिशील लोडिंग (उदाहरण के लिए Class.forName() का उपयोग करना) है, तो इनमें से कुछ समस्याएं तुरंत दिखाई नहीं दे सकती हैं।

संक्षेप में, संकलन समय पर निर्भरता को अनदेखा करने की लागत धीमी जावा विकास और कम विश्वसनीय जावा अनुप्रयोगों की होगी।

0

मैं दो वर्गों बनाया: Caller और Callee

public class Caller { 
    public void doSomething(Callee callee) { 
     callee.doSomething(); 
    } 

    public void doSame(Callee callee) { 
     callee.doSomething(); 
    } 

    public void doSomethingElse(Callee callee) { 
     callee.doSomethingElse(); 
    } 
} 

public class Callee { 
    public void doSomething() { 
    } 
    public void doSomethingElse() { 
    } 
} 

मैं इन कक्षाओं में संकलित और फिर उन्हें javap -c Callee > Callee.bc और javap -c Caller > Caller.bc साथ disassembled। - इसका क्या वर्ग जानता है और क्या विधि यहाँ लागू की जा रही है

Compiled from "Caller.java" 
public class Caller extends java.lang.Object{ 
public Caller(); 
Code: 
0: aload_0 
1: invokespecial #1; //Method java/lang/Object."<init>":()V 
4: return 

public void doSomething(Callee); 
Code: 
0: aload_1 
1: invokevirtual #2; //Method Callee.doSomething:()V 
4: return 

public void doSame(Callee); 
Code: 
0: aload_1 
1: invokevirtual #2; //Method Callee.doSomething:()V 
4: return 

public void doSomethingElse(Callee); 
Code: 
0: aload_1 
1: invokevirtual #3; //Method Callee.doSomethingElse:()V 
4: return 

} 

Compiled from "Callee.java" 
public class Callee extends java.lang.Object{ 
public Callee(); 
Code: 
0: aload_0 
1: invokespecial #1; //Method java/lang/Object."<init>":()V 
4: return 

public void doSomething(); 
Code: 
0: return 

public void doSomethingElse(); 
Code: 
0: return 

} 

संकलक के लिए विधि करने के लिए 'कॉल प्राप्त करने वाला' कहता है एक विधि हस्ताक्षर और typesafe invokevirtual कॉल से जेनरेट: यह निम्नलिखित का उत्पादन किया। यदि वह वर्ग उपलब्ध नहीं था, तो संकलक विधि हस्ताक्षर या 'invokevirtual' कैसे उत्पन्न करेगा?

एक 'invokedynamic' opcode जोड़ने के लिए एक JSR (JSR 292) है जो गतिशील आमंत्रण का समर्थन करेगा, हालांकि वर्तमान में यह JVM द्वारा समर्थित नहीं है।

संबंधित मुद्दे