जावा "?" नल की जाँच के लिए ऑपरेटर - यह क्या है? (टर्नरी नहीं!)


86

मैं एक स्लैशडॉट स्टोरी से जुड़ा एक लेख पढ़ रहा था, और इस छोटी सी ख़बर पर आया:

जावा का नवीनतम संस्करण लें, जो अंतहीन पॉइंटर परीक्षण के लिए शॉर्टहैंड सिंटैक्स की पेशकश करके नल-पॉइंटर चेकिंग को आसान बनाने की कोशिश करता है। प्रत्येक विधि के आह्वान पर एक प्रश्नवाचक चिह्न जोड़ने से स्वतः ही अशक्त बिंदुओं के लिए एक परीक्षण शामिल होता है, यदि तत्कालीन कथनों के चूहे के घोंसले की जगह, जैसे:

    public String getPostcode(Person person) {
      String ans= null;
      if (person != null) {
        Name nm= person.getName();
        if (nm!= null) {
          ans= nm.getPostcode();
        }
      }
      return ans
    } 

इसके साथ:

public String getFirstName(Person person) {
      return person?.getName()?.getGivenName();
    } 

मैंने इंटरनेट को ठीक किया है (ठीक है, मैंने "जावा प्रश्न चिह्न" पर कम से कम 15 मिनट की विविधताएं बिताईं) और कुछ भी नहीं मिला। तो, मेरा प्रश्न: क्या इस पर कोई आधिकारिक दस्तावेज है? मैंने पाया कि C # के पास एक समान ऑपरेटर ("??" ऑपरेटर) है, लेकिन मैं जिस भाषा में काम कर रहा हूं, उसके लिए दस्तावेज प्राप्त करना चाहता हूं। या, यह केवल उन सहायक ऑपरेटर का उपयोग है जो मैंने किया है। पहले कभी नहीं देखा।

धन्यवाद!

संपादित करें: लेख का लिंक: http://infoworld.com/d/developer-world/12-programming-mistakes-avoid-292


3
क्या हमारे पास लेख का लिंक कम से कम हो सकता है?
कार्ल किंचल

और स्निपेट का स्रोत?
khachik 17

5
लेख गलत है। infoworld.com/print/145292 मेरा मानना ​​है कि इसके लिए प्रोजेक्ट सिक्का जमा किया गया था। लेकिन इसका चयन नहीं किया गया था (लेख में वर्णित कारणों के लिए - यदि आप उस तरह का काम करना चाहते हैं, तो C # या कुछ का उपयोग करें), और निश्चित रूप से जावा भाषा के वर्तमान संस्करण में नहीं है।
टॉम Hawtin - tackline

4
वह C # के समान नहीं है ?? ऑपरेटर: ?? nulls coalesces, यानी A ?? B == (A != null) ? A : B। यह ऑब्जेक्ट पर किसी संपत्ति का मूल्यांकन करने के लिए प्रकट होता है यदि ऑब्जेक्ट संदर्भ शून्य नहीं है, अर्थात A?.B == (A != null) ? A.B : null
Rup

1
@Erty: @NotNull एनोटेशन के एक विशाल उपयोगकर्ता के रूप में, जो मूल रूप से मेरे द्वारा लिखे गए कोड में हर जगह है, मुझे अब तक अच्छी तरह से पता नहीं है कि NPEs क्या हैं (बुरी तरह से एपीआई का उपयोग करते समय को छोड़कर)। फिर भी मुझे यह "शॉर्टकट नोटेशन" प्यारा और दिलचस्प लगता है। बेशक लेख सही है जब यह बताता है: आखिरकार, यह समस्या की जड़ को खत्म नहीं करता है: तेज और ढीली प्रोग्रामिंग के कारण अशक्त मूल्यों का प्रसार। 'null' OOA / OOD स्तर पर मौजूद नहीं है। यह एक और Java-idiosynchratic बकवास है जिसे ज्यादातर चारों ओर काम किया जा सकता है। मेरे लिए यह हर जगह @NotNull है।
SyntaxT3rr0r

जवाबों:


78

मूल विचार ग्रूवी से आता है। यह प्रोजेक्ट कॉइन के भाग के रूप में जावा 7 के लिए प्रस्तावित किया गया था: https://wiki.openjdk.java.net/display/Coin/2009+Proposals+TOC (एल्विस और अन्य नल-सुरक्षित ऑपरेटर), लेकिन अभी तक स्वीकार नहीं किया गया है ।

संबंधित एल्विस ऑपरेटर ?: के लिए x ?: yशॉर्टहैंड बनाने का प्रस्ताव किया गया था x != null ? x : y, खासकर जब एक्स एक जटिल अभिव्यक्ति है।


3
जावा में (जहां शून्य करने के लिए कोई ऑटो-जबरदस्ती नहीं है) एक शॉर्टहैंड के लिएx!=null ? x : y
माइकल बोर्गवर्ड्ट

@ मिचेल बोर्गवर्ड: अच्छी बात है, मैं ग्रूवी शब्दार्थ के बारे में सोच रहा था।
ataylor

50
?एल्विस प्रेस्ली के हस्ताक्षर की बोली है; :बस हमेशा की तरह आंखों की एक जोड़ी का प्रतिनिधित्व करता है। शायद ?:-oअधिक स्पष्ट है ...
एंड्रीज डॉयल

4
?:0एक "नहीं शून्य, न ही 0" ऑपरेटर होना चाहिए। इसे ऐसा बनाओ।
आफ

3
प्रस्ताव को एल्विस ऑपरेटर के रूप में संदर्भित करना वास्तव में एक गलती थी। पर स्पष्टीकरण mail.openjdk.java.net/pipermail/coin-dev/2009-July/002089.html "अशक्त-सुरक्षित dereferencing" उपयोग के लिए एक बेहतर शब्द है।
जस्टिनकेएसयू

62

यह सिंटैक्स जावा में मौजूद नहीं है, और न ही यह किसी भी आगामी संस्करण में शामिल किए जाने के लिए स्लेटेड है जो मुझे पता है।


9
क्यों होता है पतन? यह उत्तर 100% सही है जहाँ तक मुझे पता है ... यदि आप कुछ अलग जानते हैं, तो कृपया कहें।
कॉलिन डी डी

6
@Webinator: यह जावा 7 या 8 में नहीं होने वाला है, और इस समय कोई अन्य "आगामी संस्करण" नहीं हैं। मुझे भी इस तरह की संभावना नहीं है कि यह इसे बनाएगा, क्योंकि यह बुरी प्रथाओं को प्रोत्साहित करता है। मुझे यह भी नहीं लगता कि "अभी तक" आवश्यक है, क्योंकि "जावा में मौजूद नहीं है" वैसा ही नहीं है क्योंकि "जावा में कभी मौजूद नहीं होगा"।
कॉलिनडी

9
@Webinator: कई पोस्टरों ने टिप्पणी की है कि एक प्रस्ताव प्रस्तुत किया गया था लेकिन अस्वीकार कर दिया गया था। इस प्रकार उत्तर 100% सटीक है। डाउन वोट का मुकाबला करने के लिए।
जेरेमीपीपी

2
@ColinD बुरा अभ्यास है जब आप इस बदसूरत कोड पर छोड़ देते हैं Optionalऔर उपयोग और mapसामान का फैसला करते हैं । हम समस्या को छुपा नहीं रहे हैं यदि मूल्य अशक्त है जिसका अर्थ है कि यह कभी-कभी शून्य होने की उम्मीद है और आपको इसे संभालना होगा। कभी-कभी डिफ़ॉल्ट मान पूरी तरह से उचित होते हैं और यह एक बुरा अभ्यास नहीं है।
एम। केज़म अखगीरी

2
जावा सिर्फ कुछ आधुनिक होने से इनकार करता है। इस भाषा को पहले ही समाप्त कर दें।
प्लेगॉन


19

की कमी को हल करने का एक तरीका "?" प्रयास-कैच के ओवरहेड के बिना जावा 8 का उपयोग करने वाला ऑपरेटर (जो कि एक NullPointerExceptionउत्पत्ति को कहीं और छिपा सकता है, जैसा कि उल्लेख किया गया है) जावा-8-स्ट्रीम शैली में "पाइप" विधियों के लिए एक वर्ग बनाने के लिए है।

public class Pipe<T> {
    private T object;

    private Pipe(T t) {
        object = t;
    }

    public static<T> Pipe<T> of(T t) {
        return new Pipe<>(t);
    }

    public <S> Pipe<S> after(Function<? super T, ? extends S> plumber) {
        return new Pipe<>(object == null ? null : plumber.apply(object));
    }

    public T get() {
        return object;
    }

    public T orElse(T other) {
        return object == null ? other : object;
    }
}

फिर, दिया गया उदाहरण बन जाएगा:

public String getFirstName(Person person) {
    return Pipe.of(person).after(Person::getName).after(Name::getGivenName).get();
}

[संपादित करें]

आगे सोचने पर, मुझे पता चला कि वास्तव में केवल मानक जावा 8 कक्षाओं का उपयोग करके ही इसे प्राप्त करना संभव है:

public String getFirstName(Person person) {
    return Optional.ofNullable(person).map(Person::getName).map(Name::getGivenName).orElse(null);
}

इस मामले में, पैरामीटर के रूप में इसे पारित "<no first name>"करने के बजाय डिफ़ॉल्ट मान (जैसे ) चुनना संभव nullहै orElse


मुझे आपका पहला उपाय बेहतर लगा। कार्यक्षमता Pipeको अनुकूलित करने के लिए आप अपनी कक्षा में कैसे सुधार करेंगे orElseताकि मैं orElseविधि के अंदर एक मनमाना गैर-अशक्त वस्तु पारित कर सकूं ?
थनोसफिशरमैन

@ThanosFisherman मैंने कक्षा में orElseविधि जोड़ी है Pipe
हेलर परेरा

7

देखें: https://blogs.oracle.com/darcy/project-coin:-the-final-five-or-so (विशेष रूप से "एल्विस और अन्य अशक्त सुरक्षित ऑपरेटर")।

परिणाम यह है कि इस सुविधा को जावा 7 के लिए माना जाता था, लेकिन इसमें शामिल नहीं किया गया था।


7

यह वास्तव में ग्रूवी के सुरक्षित-प्रसार ऑपरेटर है । आप इसे शुद्ध जावा (उदासी से) में उपयोग नहीं कर सकते हैं, इसलिए यह पोस्ट केवल गलत है (या अधिक संभावना थोड़ा भ्रामक है, अगर यह ग्रूवी को "जावा का नवीनतम संस्करण" होने का दावा कर रहा है)।


2
तो लेख गलत था - मूल जावा में वाक्य रचना मौजूद नहीं है। हम्म।
एरिटी सीडोहल

लेकिन लिंक टूट गया है
bvdb

6

जावा में सटीक सिंटैक्स नहीं है लेकिन JDK-8 के रूप में, हमारे पास हमारे निपटान में विभिन्न तरीकों के साथ वैकल्पिक एपीआई है । तो, नल सशर्त ऑपरेटर के उपयोग के साथ C # संस्करण :

return person?.getName()?.getGivenName(); 

वैकल्पिक एपीआई के साथ जावा में निम्नानुसार लिखा जा सकता है :

 return Optional.ofNullable(person)
                .map(e -> e.getName())
                .map(e -> e.getGivenName())
                .orElse(null);

यदि कोई है person, getNameया getGivenNameअशक्त है तो अशक्त वापस आ गया है।


2

उपयोग विधियों को परिभाषित करना संभव है जो जावा 8 लैम्ब्डा के साथ लगभग सुंदर तरीके से इसे हल करता है।

यह H-MAN समाधान की एक भिन्नता है, लेकिन यह कई तर्कों के साथ अतिभारित तरीकों का उपयोग करता है, पकड़ने के बजाय कई चरणों को संभालने के लिए NullPointerException

यहां तक ​​कि अगर मुझे लगता है कि यह समाधान एक तरह से अच्छा है, तो मुझे लगता है कि मैं हेल्डर परेरा के सेकंड को पसंद करता हूं क्योंकि इसके लिए किसी भी तरीके की आवश्यकता नहीं है।

void example() {
    Entry entry = new Entry();
    // This is the same as H-MANs solution 
    Person person = getNullsafe(entry, e -> e.getPerson());    
    // Get object in several steps
    String givenName = getNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.getGivenName());
    // Call void methods
    doNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.nameIt());        
}

/** Return result of call to f1 with o1 if it is non-null, otherwise return null. */
public static <R, T1> R getNullsafe(T1 o1, Function<T1, R> f1) {
    if (o1 != null) return f1.apply(o1);
    return null; 
}

public static <R, T0, T1> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, R> f2) {
    return getNullsafe(getNullsafe(o0, f1), f2);
}

public static <R, T0, T1, T2> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Function<T2, R> f3) {
    return getNullsafe(getNullsafe(o0, f1, f2), f3);
}


/** Call consumer f1 with o1 if it is non-null, otherwise do nothing. */
public static <T1> void doNullsafe(T1 o1, Consumer<T1> f1) {
    if (o1 != null) f1.accept(o1);
}

public static <T0, T1> void doNullsafe(T0 o0, Function<T0, T1> f1, Consumer<T1> f2) {
    doNullsafe(getNullsafe(o0, f1), f2);
}

public static <T0, T1, T2> void doNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Consumer<T2> f3) {
    doNullsafe(getNullsafe(o0, f1, f2), f3);
}


class Entry {
    Person getPerson() { return null; }
}

class Person {
    Name getName() { return null; }
}

class Name {
    void nameIt() {}
    String getGivenName() { return null; }
}

1

मुझे यकीन नहीं है कि यह भी काम करेगा; यदि, कहते हैं, व्यक्ति संदर्भ शून्य था, तो रनटाइम इसकी जगह क्या लेगा? एक नया व्यक्ति? इसके लिए व्यक्ति को कुछ डिफ़ॉल्ट इनिशियलाइज़ेशन की आवश्यकता होगी जो आप इस मामले में चाहते हैं। यदि आप इन प्रकार के सेटअपों की योजना नहीं बनाते हैं, तो आप अशक्त संदर्भ अपवादों से बच सकते हैं लेकिन फिर भी आपको अप्रत्याशित व्यवहार नहीं मिलेगा।

?? सी # में ऑपरेटर को "कोलेसस" ऑपरेटर कहा जा सकता है; आप कई भावों को श्रृंखलाबद्ध कर सकते हैं और यह सबसे पहले वापस आएगा जो शून्य नहीं है। दुर्भाग्य से, जावा में यह नहीं है। मुझे लगता है कि आप जो सबसे अच्छा कर सकते हैं, वह टर्नरी ऑपरेटर का उपयोग अशक्त जाँच करने के लिए और संपूर्ण अभिव्यक्ति के विकल्प का मूल्यांकन करने के लिए है यदि श्रृंखला में कोई भी सदस्य अशक्त है:

return person == null ? "" 
    : person.getName() == null ? "" 
        : person.getName().getGivenName();

आप भी कोशिश पकड़ सकते हैं:

try
{
   return person.getName().getGivenName();
}
catch(NullReferenceException)
{
   return "";
}

1
"रनटाइम इसकी जगह क्या लेगा?" ... प्रश्न पढ़ने में मदद मिल सकती है :-P यह इसे अशक्त के साथ बदल देगा। सामान्य तौर पर, यह विचार उस व्यक्ति का प्रतीत होता है? मत नामकरण शून्य का मूल्यांकन करता है यदि व्यक्ति अशक्त है या व्यक्ति के लिए है। मत नाम यदि नहीं। तो यह आपके सभी उदाहरणों में अशक्त के साथ "" की जगह बहुत पसंद है।
सदस्यता लें

1
NullReferenceException को भी फेंका जा सकता है getName()या getGivenName()जिसमें आपको पता नहीं चलेगा कि क्या आप सभी घटनाओं के लिए खाली स्ट्रिंग लौटाते हैं।
जिम्मी टी।

1
जावा में यह NullPointerException है।
तुपरुर्तुत

C # में, person?.getName()?.getGivenName() ?? ""आपके पहले उदाहरण के समतुल्य है, इस अपवाद के साथ कि यदि getGivenName()अशक्त रिटर्न देता है, तो यह अभी भी देगा""
Austin_Anderson

0

आपके पास यह है, जावा 8 में शून्य-सुरक्षित आह्वान:

public void someMethod() {
    String userName = nullIfAbsent(new Order(), t -> t.getAccount().getUser()
        .getName());
}

static <T, R> R nullIfAbsent(T t, Function<T, R> funct) {
    try {
        return funct.apply(t);
    } catch (NullPointerException e) {
        return null;
    }
}

मुझे इसे आजमाना होगा। SI को इस पूरे "वैकल्पिक" व्यवसाय के बारे में गंभीर संदेह है। एक बुरा हैक की तरह लगता है।
ggb667

3
एल्विस ऑपरेटर प्रस्ताव का पूरा उद्देश्य एक पंक्ति में ऐसा करना था। यह दृष्टिकोण ऊपर "if (! = Null) दृष्टिकोण से बेहतर नहीं है। वास्तव में मैं तर्क दूंगा कि यह बदतर है क्योंकि यह सीधे आगे नहीं है। इसके अलावा आपको ओवरहेड के कारण त्रुटियों को फेंकने और पकड़ने से बचना चाहिए।
जस्टिनकेएसयू

जैसा कि @Darron ने एक अन्य जवाब में कहा, यहां भी यही बात लागू होती है: "इस शैली के साथ समस्या यह है कि NullPointerException शायद उस जगह से नहीं आई होगी जहां से आप इसकी उम्मीद करते हैं। और इस तरह यह एक असली बग को छिपा सकता है।"
हेलर परेरा

0

अगर कोई पुराने जावा संस्करणों के लिए एक विकल्प की तलाश कर रहा है, तो आप यह एक कोशिश कर सकते हैं जो मैंने लिखा था:

/**
 * Strong typed Lambda to return NULL or DEFAULT VALUES instead of runtime errors. 
 * if you override the defaultValue method, if the execution result was null it will be used in place
 * 
 * 
 * Sample:
 * 
 * It won't throw a NullPointerException but null.
 * <pre>
 * {@code
 *  new RuntimeExceptionHandlerLambda<String> () {
 *      @Override
 *      public String evaluate() {
 *          String x = null;
 *          return x.trim();
 *      }  
 *  }.get();
 * }
 * <pre>
 * 
 * 
 * @author Robson_Farias
 *
 */

public abstract class RuntimeExceptionHandlerLambda<T> {

    private T result;

    private RuntimeException exception;

    public abstract T evaluate();

    public RuntimeException getException() {
        return exception;
    }

    public boolean hasException() {
        return exception != null;
    }

    public T defaultValue() {
        return result;
    }

    public T get() {
        try {
            result = evaluate();
        } catch (RuntimeException runtimeException) {
            exception = runtimeException;
        }
        return result == null ? defaultValue() : result;
    }

}

0

आप अपने द्वारा प्रदान किए गए कोड का परीक्षण कर सकते हैं और यह सिंटैक्स त्रुटि देगा। तो, यह जावा में समर्थित नहीं है। ग्रूवी इसका समर्थन करता है और इसे जावा 7 के लिए प्रस्तावित किया गया था (लेकिन कभी शामिल नहीं किया गया)।

हालाँकि, आप जावा 8 में दिए गए वैकल्पिक का उपयोग कर सकते हैं। यह आपको इसी तरह की रेखा पर कुछ हासिल करने में मदद कर सकता है। https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

वैकल्पिक के लिए उदाहरण कोड


0

चूंकि एंड्रॉइड लैम्बडा फंक्शंस का समर्थन नहीं करता है जब तक कि आपका स्थापित ओएस> = 24 नहीं है, हमें प्रतिबिंब का उपयोग करने की आवश्यकता है।

// Example using doIt function with sample classes
public void Test() {
    testEntry(new Entry(null));
    testEntry(new Entry(new Person(new Name("Bob"))));
}

static void testEntry(Entry entry) {
    doIt(doIt(doIt(entry,  "getPerson"), "getName"), "getName");
}

// Helper to safely execute function 
public static <T,R> R doIt(T obj, String methodName) {
    try {
       if (obj != null) 
           return (R)obj.getClass().getDeclaredMethod(methodName).invoke(obj);
    } catch (Exception ignore) {
    }
    return null;
}
// Sample test classes
    static class Entry {
        Person person;
        Entry(Person person) { this.person = person; }
        Person getPerson() { return person; }
    }

    static class Person {
        Name name;
        Person(Name name) { this.name = name; }
        Name getName() { return name; }
    }

    static class Name {
        String name;
        Name(String name) { this.name = name; }
        String getName() {
            System.out.print(" Name:" + name + " ");
            return name;
        }
    }
}

-4

यदि यह आपके लिए प्रदर्शन का मुद्दा नहीं है, तो आप लिख सकते हैं

public String getFirstName(Person person) {
  try {
     return person.getName().getGivenName();
  } catch (NullPointerException ignored) {
     return null;
  }
} 

8
इस शैली के साथ समस्या यह है कि NullPointerException शायद उस जगह से नहीं आई है जहाँ से आपने इसकी अपेक्षा की थी। और इस प्रकार यह एक असली बग को छिपा सकता है।
डार्रोन

2
@ डार्रोन, क्या आप अपने द्वारा लिखे गए एक गेटर का उदाहरण दे सकते हैं जो एक एनपीई फेंक सकता है और आप कैसे अलग तरीके से संभालना चाहेंगे?
पीटर लॉरी

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

2
कोई अपवाद नहीं है, यह एक NullPointerException होना चाहिए। आप अपने आप को एक ऐसी स्थिति से बचाने की कोशिश कर रहे हैं जिसे आप यह समझाने की कोशिश नहीं कर रहे हैं कि यह वास्तविक अनुप्रयोग में कैसे हो सकता है।
पीटर लॉरी

1
एक वास्तविक आवेदन में Personएक DB या कुछ गैर-ढेर स्मृति तक पहुँचने वाली एक प्रॉक्सी हो सकती है और कहीं एक बग हो सकती है ... बहुत यथार्थवादी नहीं, लेकिन, पीटर, मैं शर्त लगा सकता हूं कि आपने कभी भी ऊपर जैसा कोड नहीं लिखा है। ।
मातरिनस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.