2011-12-30 12 views
5

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

public T process() { 
     Iterator<Context> it = source.provideData(); 
     for(Pipe pipe : pipeline) { 
      it = pipe.processIterator(it); 
     } 
     return sink.next(it); 
    } 

यहाँ डेटा पर पुनरावर्तक समारोह वस्तुओं के बीच पारित हो जाता है, और संदर्भ संदर्भ होना चाहिए। क्या निम्न प्रकार की पाइप प्लग करने योग्य और अभी भी प्रकार की सुरक्षा बनाए रखने का कोई तरीका है?

संपादित करें: स्पष्टता के लिए, मेरे पास फ़ंक्शन ऑब्जेक्ट्स, पाइप की एक श्रृंखला है। प्रत्येक इनपुट को एक विशेष प्रकार के रूप में लेता है और दूसरे प्रकार का आउटपुट करता है। (वास्तव में इन प्रकारों पर एक पुनरावर्तक) इन्हें एक साथ जोड़ दिया जाएगा, उदाहरण के लिए, Pipe<A,B> -> Pipe<B,C> -> Pipe<C,D> -> ..., ताकि एक पाइप का आउटपुट अगले पाइप के लिए इनपुट प्रकार हो। यहां एक स्रोत भी है जो टाइप ए के इटरेटर को आउटपुट करता है, और एक सिंक जो प्रकार (पिछले पाइप का आउटपुट) स्वीकार करेगा। क्या यह चीजों को स्पष्ट बनाता है? सवाल यह है कि, इनपुट और आउटपुट प्रकारों की संगतता पर महत्वपूर्ण निर्भरता है, क्या यह सुनिश्चित करने का कोई तरीका है?

मुझे लगता है कि पाइपलाइन में फ़ंक्शन ऑब्जेक्ट्स डालने पर टाइप सुरक्षा सुनिश्चित करने का सबसे अच्छा समय हो सकता है, लेकिन मुझे यकीन नहीं है कि यह कैसे करें।

संपादित 2: मैं समारोह वस्तुओं है कि वर्तमान में नीचे की तरह दिखता है के लिए एक योजक विधि है:

public void addPipe(Pipe<?,?> pipe) { 
    pipeline.add(pipe); 
} 

मैं जाँच करने के लिए करता है, तो पहले प्रकार पैरामीटर के "अंत" के रूप में ही है करना चाहते हैं वर्तमान पाइप, और अगर अपवाद फेंक दिया? मुझे नहीं लगता कि यहां संकलन समय सुरक्षा प्राप्त करने का एक अच्छा तरीका है। वर्तमान पाइप का "अंत" तब इनपुट पाइप के दूसरे प्रकार के पैरा पर सेट किया जा सकता है। मैं जेनिक्स के साथ ऐसा करने के बारे में नहीं सोच सकता, और कक्षा की जानकारी के चारों ओर गुज़रना बहुत भयानक लगता है।

+1

टाइप मिरर पूरी तरह से सामान्य कार्यों के लिए आपके जीवन को कठिन बना सकता है। जेवीएम बाइट कंपाइलर सामान्य वर्गों के उदाहरण से प्रकार को हटा देता है, इसलिए सूची सूची बन जाती है। –

+0

क्या आप आवश्यकता को और अधिक समझ सकते हैं? यह पता लगाने में असमर्थ कि आपके पास क्या है और आप क्या चाहते हैं। –

+0

तो पाइप फ़ंक्शन ऑब्जेक्ट (कैसे ??) डालने पर टाइप सुरक्षा सुनिश्चित करने के लिए सबसे अच्छा तरीका होगा और इस उपरोक्त विधि पर चेतावनियों को दबाएं? – downer

उत्तर

7

यहां ऐसा करने का एक तरीका है। रन विधि टाइपएफ़ नहीं है, लेकिन यह देखते हुए कि पाइप को जोड़ने का एकमात्र तरीका इसे एक प्रकार से सुरक्षित तरीके से करना है, पूरी श्रृंखला टाइप-सुरक्षित है।

public class Chain<S, T> { 
    private List<Pipe<?, ?>> pipes; 

    private Chain() { 
    } 

    public static <K, L> Chain<K, L> start(Pipe<K, L> pipe) { 
     Chain<K, L> chain = new Chain<K, L>(); 
     chain.pipes = Collections.<Pipe<?, ?>>singletonList(pipe);; 
     return chain; 
    } 

    public <V> Chain<S, V> append(Pipe<T, V> pipe) { 
     Chain<S, V> chain = new Chain<S, V>(); 
     chain.pipes = new ArrayList<Pipe<?, ?>>(pipes); 
     chain.pipes.add(pipe); 
     return chain; 
    } 

    @SuppressWarnings({ "rawtypes", "unchecked" }) 
    public T run(S s) { 
     Object source = s; 
     Object target = null; 
     for (Pipe p : pipes) { 
      target = p.transform(source); 
      source = target; 
     } 
     return (T) target; 
    } 

    public static void main(String[] args) { 
     Pipe<String, Integer> pipe1 = new Pipe<String, Integer>() { 
      @Override 
      public Integer transform(String s) { 
       return Integer.valueOf(s); 
      } 
     }; 
     Pipe<Integer, Long> pipe2 = new Pipe<Integer, Long>() { 
      @Override 
      public Long transform(Integer s) { 
       return s.longValue(); 
      } 
     }; 
     Pipe<Long, BigInteger> pipe3 = new Pipe<Long, BigInteger>() { 
      @Override 
      public BigInteger transform(Long s) { 
       return new BigInteger(s.toString()); 
      } 
     }; 
     Chain<String, BigInteger> chain = Chain.start(pipe1).append(pipe2).append(pipe3); 
     BigInteger result = chain.run("12"); 
     System.out.println(result); 
    } 
} 
+0

वास्तव में अच्छा है, और सभी अच्छे समाधानों की तरह, यह ज्ञात होने के बाद यह स्पष्ट है :) – downer

+0

आपके उत्तर के लिए धन्यवाद :) मैं आपके [github प्रोजेक्ट] (https://github.com/smartorigin/Dynamic-Workflow) को आपके आधार पर बना देता हूं जवाब है कि async कार्यक्षमता और समानांतर निष्पादन जोड़ें। उम्मीद है कि यह किसी की मदद करेगा। –

0

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

public class Chain<IN, MEDIAL, OUT> { 
    private final Chain<IN, ?, MEDIAL> head; 
    private final Transformer<MEDIAL, OUT> tail; 

    public static <I, O> Chain<I, I, O> makeHead(@Nonnull Transformer<I, O> tail) { 
     return new Chain<>(null, tail); 
    } 

    public static <I, M, O> Chain<I, M, O> append(@Nonnull Chain<I, ?, M> head, @Nonnull Transformer<M, O> tail) { 
     return new Chain<>(head, tail); 
    } 

    private Chain(@Nullable Chain<IN, ?, MEDIAL> head, @Nonnull Transformer<MEDIAL, OUT> tail) { 
     this.head = head; 
     this.tail = tail; 
    } 

    public List<OUT> run(List<IN> input) { 
     List<OUT> allResults = new ArrayList<>(); 

     List<MEDIAL> headResult; 
     if (head == null) { 
      headResult = (List<MEDIAL>) input; 
     } else { 
      headResult = head.run(input); 
     } 

     for (MEDIAL in : headResult) { 
      // try/catch here 
      allResults.addAll(tail.transform(in)); 
     } 

     return allResults; 
    } 

    public static void main(String[] args) { 

     Transformer<String, Integer> pipe1 = new Transformer<String, Integer>() { 
      @Override 
      public List<Integer> transform(String s) { 
       return Collections.singletonList(Integer.valueOf(s) * 3); 
      } 
     }; 
     Transformer<Integer, Long> pipe2 = new Transformer<Integer, Long>() { 
      @Override 
      public List<Long> transform(Integer s) { 
       return Collections.singletonList(s.longValue() * 5); 
      } 
     }; 
     Transformer<Long, BigInteger> pipe3 = new Transformer<Long, BigInteger>() { 
      @Override 
      public List<BigInteger> transform(Long s) { 
       return Collections.singletonList(new BigInteger(String.valueOf(s * 7))); 
      } 
     }; 
     Chain<String, ?, Integer> chain1 = Chain.makeHead(pipe1); 
     Chain<String, Integer, Long> chain2 = Chain.append(chain1, pipe2); 
     Chain<String, Long, BigInteger> chain3 = Chain.append(chain2, pipe3); 
     List<BigInteger> result = chain3.run(Collections.singletonList("1")); 
     System.out.println(result); 
    } 
} 
+0

जाहिर है, यदि आप जावा 8 का उपयोग करने में सक्षम हैं, तो आप इसके बजाय स्ट्रीम का उपयोग करना चाहते हैं। या, आरएक्सजेवा की तरह कुछ। – Shannon

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