java - जेनेरिक के जेनेरिक कैसे काम करते हैं?




generics wildcard (4)

जबकि मैं जेनेरिक के कुछ कोने-मामलों को समझता हूं, मुझे निम्नलिखित उदाहरण के साथ कुछ याद आ रहा है।

मेरे पास निम्न वर्ग है

1 public class Test<T> {
2   public static void main(String[] args) {
3     Test<? extends Number> t = new Test<BigDecimal>();
4     List<Test<? extends Number>> l =Collections.singletonList(t);
5   }
6 }

लाइन 4 मुझे त्रुटि देता है

Type mismatch: cannot convert from List<Test<capture#1-of ? extends Number>> 
to List<Test<? extends Number>>`. 

जाहिर है, संकलक सोचता है कि अलग ? वास्तव में बराबर नहीं हैं। जबकि मेरी आंत महसूस मुझे बताती है, यह सही है।

क्या कोई ऐसा उदाहरण प्रदान कर सकता है जहां लाइन 4 कानूनी था तो मुझे रनटाइम-त्रुटि मिलेगी?

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

भ्रम से बचने के लिए, मैंने कंक्रीट असाइनमेंट द्वारा लाइन 3 में =null को प्रतिस्थापित किया


कारण यह है कि संकलक यह नहीं जानता कि आपके वाइल्डकार्ड प्रकार एक ही प्रकार के हैं।

यह भी नहीं जानता कि आपका उदाहरण null । यद्यपि null सभी प्रकार के सदस्य हैं, संकलक केवल घोषित प्रकारों को मानता है, न कि प्रकार की जांच के दौरान चर के मूल्य में क्या हो सकता है।

यदि कोड निष्पादित किया गया है, तो यह अपवाद नहीं करेगा, लेकिन यह केवल इसलिए है क्योंकि मान शून्य है। अभी भी एक संभावित प्रकार की मेल नहीं है, और यही है कि कंपाइलर का काम है - प्रकार के विसंगतियों को अस्वीकार करना।


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

इसलिए यदि आप इसके लिए <T> निर्दिष्ट करके सिंगलटनलिस्ट के आमंत्रण से अनुमान हटाते हैं:

List<Test<? extends Number>> l = Collections.<Test<? extends Number>>singletonList(t);

यह बढ़िया काम करता है। जेनरेट कोड आपके कॉल कानूनी होने के अलावा अलग नहीं है, यह केवल कंपाइलर की एक सीमा है जिसे वह स्वयं ही नहीं समझ सकता है।

नियम जो एक अनुमान कैप्चर और कैप्चर बनाता है वह संगत नहीं है, इस ट्यूटोरियल उदाहरण को संकलित करने और फिर रनटाइम पर उड़ने से रोकता है:

public static void swap(List<? extends Number> l1, List<? extends Number> l2) {
    Number num = l1.get(0);
    l1.add(0, l2.get(0));
    l2.add(0, num);
}

हां भाषा विनिर्देश और संकलक शायद इसके अलावा आपके उदाहरण को बताने के लिए और अधिक परिष्कृत हो सकता है, लेकिन यह नहीं है और यह काम करने के लिए काफी आसान है।


शायद यह संकलक की समस्या की व्याख्या कर सकता है:

List<? extends Number> myNums = new ArrayList<Integer>();

यह जेनेरिक वाइल्डकार्ड सूची संख्या से विस्तारित किसी भी तत्व को पकड़ सकती है। तो इसे एक इंटीजर सूची असाइन करने के लिए ठीक है। हालांकि अब मैं myNums में एक डबल जोड़ सकता हूं क्योंकि डबल नंबर से भी विस्तार कर रहा है जो रनटाइम समस्या का कारण बनता है। तो संकलक myNums के लिए प्रत्येक लेखन पहुंच को रोकता है और मैं केवल उस पर पढ़ने के तरीकों का उपयोग कर सकता हूं, क्योंकि मुझे केवल इतना पता है कि मुझे जो भी मिलता है उसे नंबर पर डाला जा सकता है।

और इसलिए संकलक ऐसी चीजों के बारे में शिकायत कर रहा है जो आप ऐसे वाइल्डकार्ड जेनेरिक के साथ कर सकते हैं। कभी-कभी वह उन चीज़ों के बारे में पागल होता है जिन्हें आप सुनिश्चित कर सकते हैं कि वे सुरक्षित और ठीक हैं।

लेकिन सौभाग्य से इस त्रुटि को पाने के लिए एक चाल है ताकि आप अपने आप पर परीक्षण कर सकें जो शायद इसे तोड़ सकता है:

public static void main(String[] args) {

    List<? extends Number> list1 = new ArrayList<BigDecimal>();
    List<List<? extends Number>> list2 = copyHelper(list1);


}

private static <T> List<List<T>> copyHelper(List<T> list) {
    return Collections.singletonList(list);

}

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

आइए कोशिश करें और अपना उदाहरण किसी अन्य तरीके से देखें (आइए दो प्रकार का उपयोग करें जो संख्या का विस्तार करते हैं लेकिन बहुत अलग व्यवहार करते हैं)। निम्नलिखित कार्यक्रम पर विचार करें:

import java.math.BigDecimal;
import java.util.*;

public class q16449799<T extends Number> {
  public T val;

  public static void main(String ... args) {
    q16449799<BigDecimal> t = new q16449799<>();
    t.val = new BigDecimal(Math.PI);

    List<q16449799<BigDecimal>> l = Collections.singletonList(t);
    for(q16449799<BigDecimal> i : l) {
      System.out.println(i.val);
    }
  }
}

यह आउटपुट (जैसा कि एक उम्मीद करेगा):

3.141592653589793115997963468544185161590576171875

अब आपके द्वारा प्रस्तुत कोड मानने से संकलक त्रुटि उत्पन्न नहीं हुई है:

import java.math.BigDecimal;
import java.util.concurrent.atomic.AtomicLong;

public class q16449799<T extends Number> {
  public T val;

  public static void main(String ... args) {
    q16449799<BigDecimal> t = new q16449799<>();
    t.val = new BigDecimal(Math.PI);

    List<q16449799<AtomicLong>> l = Collections.singletonList(t);
    for(q16449799<AtomicLong> i : l) {
      System.out.println(i.val);
    }
  }
}

आप आउटपुट की अपेक्षा क्या करेंगे? आप उचित रूप से एक परमाणुओं के लिए बिगडेसिमल नहीं डाल सकते हैं (आप एक बिगडिसीमल के मूल्य से परमाणु लोंग बना सकते हैं, लेकिन कास्टिंग और निर्माण अलग-अलग चीजें हैं और जेनिक्स को सुनिश्चित करने के लिए संकलित समय चीनी के रूप में लागू किया जाता है ताकि यह सुनिश्चित हो सके कि कलाकार सफल हैं)। @ केनीटीएम की टिप्पणी के लिए, जब आप प्रारंभिक उदाहरण में एक ठोस प्रकार खोज रहे हैं लेकिन इसे आजमाएं और संकलित करें:

import java.math.BigDecimal;
import java.util.*;

public class q16449799<T> {
  public T val;

  public static void main(String ... args) {
    q16449799<? extends Number> t = new q16449799<BigDecimal>();

    t.val = new BigDecimal(Math.PI);

    List<q16449799<? extends Number>> l = Collections.<q16449799<? extends Number>>singletonList(t);
    for(q16449799<? extends Number> i : l) {
      System.out.println(i.val);
    }
  }
}

यह आपके द्वारा प्रयास किए जाने वाले क्षण को त्रुटि देगा और t.val को मान सेट t.val







capture