Java8 में शून्य (शून्य नहीं) विधियों के लिए फ़ंक्शन प्रकार कैसे निर्दिष्ट करें?


143

मैं यह जानने के लिए जावा 8 के साथ खेल रहा हूं कि प्रथम श्रेणी के नागरिक कैसे काम करते हैं। मेरे पास निम्नलिखित स्निपेट हैं:

package test;

import java.util.*;
import java.util.function.*;

public class Test {

    public static void myForEach(List<Integer> list, Function<Integer, Void> myFunction) {
      list.forEach(functionToBlock(myFunction));
    }

    public static void displayInt(Integer i) {
      System.out.println(i);
    }


    public static void main(String[] args) {
      List<Integer> theList = new ArrayList<>();
      theList.add(1);
      theList.add(2);
      theList.add(3);
      theList.add(4);
      theList.add(5);
      theList.add(6);
      myForEach(theList, Test::displayInt);
    }
}

मैं जो करने की कोशिश कर रहा हूं वह एक विधि संदर्भ का उपयोग करके विधि के displayIntलिए विधि है myForEach। कंपाइलर निम्नलिखित त्रुटि पैदा करता है:

src/test/Test.java:9: error: cannot find symbol
      list.forEach(functionToBlock(myFunction));
                   ^
  symbol:   method functionToBlock(Function<Integer,Void>)
  location: class Test
src/test/Test.java:25: error: method myForEach in class Test cannot be applied to given ty
pes;
      myForEach(theList, Test::displayInt);
      ^
  required: List<Integer>,Function<Integer,Void>
  found: List<Integer>,Test::displayInt
  reason: argument mismatch; bad return type in method reference
      void cannot be converted to Void

संकलक शिकायत करता है कि void cannot be converted to Void। मुझे पता नहीं है कि इस तरह के हस्ताक्षर में फ़ंक्शन इंटरफ़ेस के प्रकार को कैसे निर्दिष्ट किया जाए myForEachताकि कोड संकलित हो। मैं जानता हूँ कि मैं बस की वापसी प्रकार बदल सकता है displayIntकरने के लिए Voidऔर फिर लौटने null। हालाँकि, ऐसी परिस्थितियाँ हो सकती हैं, जहाँ मैं उस विधि को बदलना संभव नहीं हूँ जहाँ मैं कहीं और से गुजरना चाहता हूँ। क्या displayIntइसका पुन: उपयोग करने का एक आसान तरीका है?

जवाबों:


244

आप गलत इंटरफ़ेस प्रकार का उपयोग करने का प्रयास कर रहे हैं। टाइप फंक्शन इस मामले में उचित नहीं है क्योंकि यह एक पैरामीटर प्राप्त करता है और एक रिटर्न वैल्यू है। इसके बजाय आपको कंज्यूमर का इस्तेमाल करना चाहिए (पहले ब्लॉक के रूप में जाना जाता है) का

फ़ंक्शन प्रकार के रूप में घोषित किया गया है

interface Function<T,R> {
  R apply(T t);
}

हालाँकि, उपभोक्ता प्रकार वह है जिसके साथ आप देख रहे हैं:

interface Consumer<T> {
   void accept(T t);
}

जैसे, कंज्यूमर उन तरीकों के अनुकूल है जो एक टी प्राप्त करते हैं और कुछ नहीं (शून्य) वापस करते हैं। और यही आप चाहते हैं।

उदाहरण के लिए, यदि मैं एक सूची में सभी तत्व प्रदर्शित करना चाहता था, तो मैं केवल इसके लिए एक लंबोदर अभिव्यक्ति के साथ एक उपभोक्ता बना सकता था:

List<String> allJedi = asList("Luke","Obiwan","Quigon");
allJedi.forEach( jedi -> System.out.println(jedi) );

आप ऊपर देख सकते हैं कि इस मामले में, लैम्ब्डा अभिव्यक्ति एक पैरामीटर प्राप्त करता है और इसका कोई वापसी मूल्य नहीं है।

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

मैं विधि संदर्भ के विभिन्न प्रकार इस्तेमाल कर सकते हैं, लेकिन इस मामले में का उपयोग करके एक वस्तु विधि संदर्भ के लाभ उठा सकते printlnमें विधि System.outवस्तु, इस तरह:

Consumer<String> block = System.out::println

या मैं बस कर सकता था

allJedi.forEach(System.out::println);

printlnविधि उपयुक्त है, क्योंकि यह एक मूल्य प्राप्त करता है और एक वापसी प्रकार शून्य है, जैसेaccept उपभोक्ता में विधि।

इसलिए, आपके कोड में, आपको अपनी विधि हस्ताक्षर को कुछ हद तक बदलना होगा:

public static void myForEach(List<Integer> list, Consumer<Integer> myBlock) {
   list.forEach(myBlock);
}

और फिर आपको अपने मामले में एक स्थिर विधि संदर्भ का उपयोग करके उपभोक्ता बनाने में सक्षम होना चाहिए:

myForEach(theList, Test::displayInt);

अंततः, आप अपनी myForEachविधि से पूरी तरह छुटकारा पा सकते हैं और बस कर सकते हैं:

theList.forEach(Test::displayInt);

प्रथम श्रेणी के नागरिकों के रूप में कार्यों के बारे में

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


1
वैसे, JDK 8 एपीआई की नवीनतम रिलीज के Blockलिए बदल दिया गया थाConsumer
एडविन डेलोरजो

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

7
और का सही संस्करण Function<Void, Void>है Runnable
ऑरेंजडॉग सेप

2
@ ऑरेंजडॉग यह पूरी तरह सच नहीं है। टिप्पणियों में Runnableऔर Callableइंटरफेस के लिए यह लिखा है कि उनका उपयोग थ्रेड्स के साथ संयोजन में किया जाना चाहिए । इसका कष्टप्रद परिणाम यह है कि कुछ स्थिर विश्लेषण उपकरण (जैसे सोनार) शिकायत करेंगे यदि आप run()सीधे विधि कहते हैं।
डेनिस

एक जावा बैकग्राउंड से पूरी तरह से आते हुए, मुझे आश्चर्य है कि जावा के बाद से कार्यात्मक इंटरफेस और लैम्ब्डा की शुरुआत के बाद भी जावा उन मामलों में विशेष रूप से क्या है जहां जावा प्रथम श्रेणी के नागरिकों के रूप में कार्य नहीं कर सकता है?
विवेक सेठी

21

जब आपको किसी फ़ंक्शन को तर्क के रूप में स्वीकार करने की आवश्यकता होती है, जो कोई तर्क नहीं लेता है और कोई परिणाम नहीं देता है (शून्य), मेरी राय में यह अभी भी सबसे अच्छा है जैसे कुछ

  public interface Thunk { void apply(); }

अपने कोड में कहीं मेरे कार्यात्मक प्रोग्रामिंग पाठ्यक्रमों में ऐसे कार्यों का वर्णन करने के लिए 'थंक' शब्द का इस्तेमाल किया गया था। यह java.util.function में क्यों नहीं है यह मेरी समझ से परे है।

अन्य मामलों में मुझे लगता है कि जब java.util.function के पास कुछ ऐसा है जो मुझे हस्ताक्षर से मेल खाता है - यह अभी भी हमेशा सही नहीं लगता है जब इंटरफ़ेस का नामकरण मेरे कोड में फ़ंक्शन के उपयोग से मेल नहीं खाता है। मुझे लगता है कि यह एक ऐसा ही बिंदु है जो 'रननेबल' के बारे में यहाँ कहीं और बना है - जो कि थ्रेड क्लास से जुड़ा हुआ शब्द है - इसलिए जब तक उसे मेरे हस्ताक्षर की आवश्यकता हो सकती है, तब भी पाठक को भ्रमित करने की संभावना है।


विशेष रूप से Runnableचेक किए गए अपवादों की अनुमति नहीं देता है, जिससे यह कई अनुप्रयोगों के लिए बेकार हो जाता है। चूंकि Callable<Void>यह उपयोगी नहीं है, इसलिए आपको अपने स्वयं के परिभाषित करने की आवश्यकता है। बदसूरत।
जेसी ग्लिक

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

0

के Voidबजाय voidऔर के लिए वापसी प्रकार सेट करेंreturn null

// Modify existing method
public static Void displayInt(Integer i) {
    System.out.println(i);
    return null;
}

या

// Or use Lambda
myForEach(theList, i -> {System.out.println(i);return null;});

-2

मुझे लगता है कि आपको इसके बजाय उपभोक्ता इंटरफ़ेस का उपयोग करना चाहिए Function<T, R>

एक उपभोक्ता मूल रूप से एक कार्यात्मक इंटरफ़ेस है जो एक मूल्य को स्वीकार करने और कुछ नहीं देने के लिए डिज़ाइन किया गया है (अर्थात शून्य)

आपके मामले में, आप अपने कोड में इस तरह से एक उपभोक्ता बना सकते हैं:

Consumer<Integer> myFunction = x -> {
    System.out.println("processing value: " + x);    
    .... do some more things with "x" which returns nothing...
}

फिर आप myForEachनीचे दिए गए स्निपेट के साथ अपना कोड बदल सकते हैं :

public static void myForEach(List<Integer> list, Consumer<Integer> myFunction) 
{
  list.forEach(x->myFunction.accept(x));
}

आप MyFunction को प्रथम श्रेणी की वस्तु मानते हैं।


2
यह मौजूदा उत्तरों से आगे क्या जोड़ता है?
मैथ्यू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.