2011-02-14 17 views
10

मैं Exception के भीतर सामान्य वस्तुओं का संग्रह संग्रहीत करने की कोशिश कर रहा हूं और जेनिक्स को समझने में परेशानी हो रही है। विशेष रूप से, मैं हाइबरनेट वैलिडेटर का उपयोग कर रहा हूं और एप्लिकेशन की दूसरी परत में प्रसंस्करण के लिए अपवाद के भीतर उल्लंघन की एकत्रित सूची को सहेजना चाहता हूं। यहाँ एक उदाहरण है:अपवाद के तर्कों में जेनिक्स का उपयोग

Set<ConstraintViolation<User>> violations = validator.validate(user); 
if (violations.size() > 0) { 
    throw new ValidationException("User details are invalid", violations); 
} 

ग्रहण में, throws लाइन निर्माता अपरिभाषित दिखाई दे रहा है और सुझाव है मैं ValidationException(String, Set<ConstraintViolation<User>> के निर्माता हस्ताक्षर बदल जाते हैं। यहाँ ValidationException है:

public class ValidationException extends Exception { 
    private Set<ConstraintViolation<?>> violations; 

    public ValidationException() { 
    } 
    public ValidationException(String msg) { 
     super(msg); 
    } 
    public ValidationException(String msg, Throwable cause) { 
     super(msg, cause); 
    } 
    public ValidationException(String msg, Set<ConstraintViolation<?>> violations) { 
     super(msg); 
     this.violations = violations; 
    } 
    public Set<ConstraintViolation<?>> getViolations() { 
     return violations; 
    } 
} 

हालांकि, मैं ValidationException सामान्य रखने के लिए इतना है कि मैं सिर्फ User सत्यापन से अधिक के लिए उपयोग कर सकते हैं चाहता हूँ। मैंने Set<ConstraintViolation<? extends Object>> भी कोशिश की है, लेकिन एक ही परिणाम प्राप्त करें।

क्या मैं ऐसा करने का प्रयास कर रहा हूं?

public class ValidationException extends Exception { 
    private Set<ConstraintViolation<?>> violations; 

    @SuppressWarning("unchecked") 
    public <T> ValidationException(String msg, Set<ConstraintViolation<T>> violations) { 
     super(msg); 
     this.violations = (Set<ConstraintViolation<?>>)(Set<?>) violations; 
    } 
} 

जहां तक ​​मैं समझता हूँ, अनियंत्रित डाली, इस मामले में पूरी तरह से सुरक्षित है ताकि @SuppressWarning("unchecked") बिल्कुल कानूनी है:

+0

तुम क्यों ConstrainsViolation की क्या ज़रूरत है पहली जगह में आम हो सकते? क्या आप प्रत्येक संभावित उल्लंघन के लिए एक सारणीबेस विरासत में एक अलग वर्ग नहीं बना सकते हैं? – Falcon

+1

मैंने सोचा कि एक सामान्य अपवाद प्रकार का उपयोग करना एक अच्छा विचार होगा, लेकिन जैसा कि @axtavt उल्लेख किया गया है, अपवाद सामान्य नहीं हो सकते हैं। यह FAQ देखें: http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ301 – Tauren

+0

@ फाल्कन: 'प्रतिबंधों का उल्लंघन' हाइबरनेट सत्यापन पैकेज का हिस्सा है और जेनेरिक का उपयोग करता है। यह मेरी खुद की एक वर्ग नहीं है। – Tauren

उत्तर

12

आप Set<? extends ConstraintViolation<?>> के रूप में उल्लंघन पैरामीटर सेट की घोषणा करने की जरूरत है: के रूप में उम्मीद

public ValidationException(String msg, 
          Set<? extends ConstraintViolation<?>> violations) { 
    super(msg); 
    this.violations = Collections.unmodifiableSet(
     new HashSet<ConstraintViolation<?>>(violations)); 
} 

तो सब कुछ काम करना चाहिए।

आपके पास दिए गए Set की रक्षात्मक रूप से प्रतिलिपि बनाने का अतिरिक्त लाभ है, यह सुनिश्चित करना कि अपवाद का आंतरिक Set बदला नहीं जा सकता है।

+0

धन्यवाद, यह अच्छा और साफ दिखता है! – Tauren

1

एक बदसूरत दृष्टिकोण अनियंत्रित डाली उपयोग करने के लिए किया जाएगा।

दूसरी तरफ, इस कन्स्ट्रक्टर को Set<ConstraintViolation<?>> पैरामीटर के रूप में नहीं कहा जा सकता है।

+0

बदसूरत सही है! लेकिन ऐसा लगता है कि यह काम करता है। अब संकलन त्रुटियां नहीं मिल रही हैं। धन्यवाद! – Tauren

+0

चाहे कास्ट सुरक्षित है, अपवाद ऑब्जेक्ट के बाहर सेट (और कैसे) सेट का उपयोग किया जाता है। 'सेट >' सेट <कॉन्स्ट्रेनेशन उल्लंघन > 'का एक सुपरटेप नहीं है, क्योंकि बाद वाला व्यक्ति केवल' कॉन्स्ट्रेनटिओशन '(और इसके उपयोगकर्ता इस मामले पर निर्भर हो सकते हैं) के तत्वों को स्वीकार करते हैं, जबकि पूर्व स्वीकार करते हैं 'किसी भी प्रकार (यहां तक ​​कि मिश्रित) की रोकथाम का उल्लंघन (जिसका अर्थ है कि आप इस सेट में' प्रतिबंध उल्लंघन 'डाल सकते हैं)। अपवाद के लिए इस उपयोग में माना जाता है कि अपवाद के निर्माता इसे बनाते हैं और पकड़ने वाला केवल पुनरावृत्त होता है, इसलिए यह वास्तव में कोई फर्क नहीं पड़ता। –

0

मुझे लगता है कि समस्या यह है कि Set<ConstraintViolation<User>> में केवल ConstraintViolation<User> प्रकार की वस्तुएं हो सकती हैं, जबकि आपके तर्क प्रकार Set<ConstraintViolation<?>> में किसी भी प्रकार का उल्लंघन हो सकता है (इसलिए, यह एक मिश्रित संग्रह है)। इस प्रकार, यह एक उप प्रकार नहीं है। मुझे लगता है कि (कोशिश नहीं की) आप के रूप में

public <T> ValidationException(String msg, Set<ConstraintViolation<T>> violations); 

निर्माता की घोषणा कर सकता है, लेकिन फिर आप अभी भी समस्या है कि आपके अपवाद में चर इस तरह के एक प्रकार नहीं कर सकते हैं। आप पैरामीटर की सामग्री को नए सेट में कॉपी कर सकते हैं, इसे लपेटने के लिए Collections.unmodifiedSet() का उपयोग करें, या इसके आसपास पाने के लिए एक्स्टाव द्वारा उल्लिखित बदसूरत कास्ट करें। यहाँ मेरी पसंदीदा तरीका:

public <T> ValidationException(String msg, Set<ConstraintViolation<T>> violations) { 
    this.violations = Collections.unmodifiableSet(violations); 
} 
1

मान लीजिए कि आवश्यकता यह है कि सेट एकरूप होना चाहिए - violations के लिए Set<ConstraintViolation<X>> टाइप होना चाहिए। ,

public class ValidationException<T> extends Exception 
    Set<ConstraintViolation<T>> violations; 
    public ValidationException(String msg, Set<ConstraintViolation<T>> violations) 
बेशक

, जावा फेंकने योग्य के उपप्रकार के लिए अनुमति नहीं देता है प्रकार प्रणाली से परे कारणों के लिए:

कि ऐसा करने का सबसे प्राकृतिक तरीका ValidationException सामान्य बनाना है।यह हमारी गलती नहीं है, इसलिए हम कुछ वैकल्पिक हल की खोज करने का दोषी नहीं हैं:

public class ValidationException extends Exception 
{ 
    static class SetConstraintViolation<T> extends HashSet<ConstraintViolation<T>> 
    { 
     SetConstraintViolation(Set<ConstraintViolation<T>> violations) 
     { 
      super(violations); 
     } 
    } 

    // this is homogeneous, though X is unknown 
    private SetConstraintViolation<?> violations; 

    public <T> ValidationException(String msg, Set<ConstraintViolation<T>> violations) 
    { 
     super(msg); 
     this.violations = new SetConstraintViolation<T>(violations); 
    } 

    public <T> Set<ConstraintViolation<T>> getViolations() 
    { 
     return (Set<ConstraintViolation<T>>)violations; 
    } 
} 

void test() 
{ 
    Set<ConstraintViolation<User>> v = ...; 
    ValidationException e = new <User>ValidationException("", v); 
    Set<ConstraintViolation<User>> v2 = e.getViolations(); 
    Set<ConstraintViolation<Pswd>> v3 = e.getViolations(); 
    Set<? extends ConstraintViolation<?>> v4 = e.getViolations(); 
} 

नोट: getViolations() में डाली केवल सुरक्षित है, यदि कॉल साइट सही T आपूर्ति, v2 के मामले में। V3 के मामले में, कास्ट गलत है - संकलक ने हमें व्यर्थ में चेतावनी नहीं दी।

कॉल साइट शायद नहीं जानता है, और v4 के मामले में सटीक T की परवाह नहीं करता है। कॉल साइट उल्लंघनों, एक निश्चित अज्ञात प्रकार का एक सजातीय संग्रह, वाइल्डकार्ड के साथ एक सामान्य सामान्य रूप से टाइप करने के लिए जा सकती है। यह काफी अजीब है। यदि v4 सबसे अधिक उपयोग करने वाला मामला है, तो हमें एक विधि प्रदान करनी चाहिए जो Set<ConstraintViolation<?>> लौटाती है। हम सीधे violations वापस नहीं कर सकते हैं, यह असुरक्षित है। एक प्रति आवश्यक है। यदि v4 एकमात्र उपयोग केस है, तो यह समाधान वास्तव में पिछले उत्तरदाताओं द्वारा प्रस्तावित एक ही समाधान बन जाता है।

+0

बहुत गहन जवाब के लिए धन्यवाद! आपका समाधान अतिरिक्त जटिलता जोड़ने लगता है। क्या कोई कारण है कि यह @ColinD द्वारा सुझाए गए कुछ आसान की तुलना में बेहतर दृष्टिकोण है? – Tauren

+0

मैंने सोचा प्रयोग के लिए सजातीय आवश्यकता लगाई, अगर कुछ और नहीं। शायद यह नहीं है कि आप किस चीज की परवाह करते हैं। – irreputable

0

मेरा मानना ​​है कि जावा throwable सामान्य नहीं बनाया जा सकता है, क्योंकि catch ब्लॉक मिटाए जाने के कारण ब्लॉक अपवादों के सामान्य पैरामीटर का परीक्षण नहीं कर सकता है। आपको इसे पुराने तरीके से करना होगा, जिसमें कास्ट और क्या नहीं होगा।

आप एक विधि जोड़ सकते हैं स्वतः डाली करने के लिए:

 
class ConstraintViolation extends SomeException { 
  Constraint c; 
    «T extends Constraint» T getConstraint() { return (T) c}; 
} 

… 
UserConstraint uc = theViolation.getConstraint(); 

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

0

आप कर सकता है:

Set<ConstraintViolation<User>> violations = validator.validate(user); 
if (violations.size() > 0) { 
    throw new ValidationException("User details are invalid", (Set<ConstraintViolation<?>>) (Set<? extends ConstraintViolation<?>>) violations); 
} 
संबंधित मुद्दे