2011-02-14 67 views
10

我試圖在Exception中存儲泛型對象的集合,並且遇到了找出泛型的麻煩。具體而言,我使用Hibernate驗證器,並希望將收集的違規列表保存在異常中,以便在應用程序的另一層進行處理。這裏有一個例子:在異常的參數中使用泛型

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

在Eclipse中,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>>,但得到相同的結果。

有沒有辦法完成我想要做的事情?

+0

爲什麼你需要的ConstrainsViolation是通用擺在首位?難道你不能只爲每個可能的違規行爲創建一個繼承AbstractBase的單獨的類嗎? – Falcon 2011-02-14 13:17:52

+1

我認爲使用泛型異常類型會是一個好主意,但是像@axtavt提到的那樣,異常不能是泛型的。請參閱此常見問題解答:http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ301 – Tauren 2011-02-14 13:31:23

+0

@Falcon:`ConstraintsViolation`是Hibernate驗證包的一部分,它使用泛型。這不是我自己的一類。 – Tauren 2011-02-14 13:32:49

回答

12

您需要聲明設置參數的侵權行爲爲Set<? extends ConstraintViolation<?>>

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

然後一切都應該按預期工作。

這還具有防禦性地複製Set給予您的額外好處,確保異常的內部Set不能更改。

1

一個醜陋的做法是使用未經檢查的強制轉換:

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")是絕對合法的。

從另一方面來說,不能使用Set<ConstraintViolation<?>>作爲參數調用此構造函數。

+0

醜是對的!但它看起來像是有效的。不再發生編譯錯誤。謝謝! – Tauren 2011-02-14 13:46:49

0

我認爲問題是Set<ConstraintViolation<User>>只能包含ConstraintViolation<User>類型的對象,而您的參數類型Set<ConstraintViolation<?>>可以包含任何類型的違規(所以它是一個混合集合)。因此,這不是一個子類型。我覺得(沒試過),你可以聲明構造爲

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

但你仍然有你的例外變量不能有這樣一種類型的問題。您可以將參數的內容複製到新的Set中,使用Collections.unmodifiedSet()來包裝它,或者執行axtavt提到的醜陋演員來解決此問題。在這裏我的首選方式:

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

假設要求是該組必須是同質的 - 對於某些Xviolations必須是Set<ConstraintViolation<X>>

最自然的方式來做到這一點是讓ValidationException通用:

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

當然,Java不允許,對於Throwable的亞型,超出類型系統的原因。這不是我們的錯,所以我們沒有罪發明了一些變通方法:

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的情況下,演員陣容是錯誤的 - 編輯沒有白白警告我們。

調用網站可能不知道,也不關心,確切的T,如在v4的情況下。呼叫站點可以將違規行爲(即某種未知類型的同質集合)轉換爲具有通配符的更一般的只讀類型。這非常尷尬。如果案例v4是最常見的使用案例,我們應該提供一種簡單返回Set<ConstraintViolation<?>>的方法。我們不能直接返回violations,這是不安全的。一份副本是必需的。如果v4是唯一的使用案例,那麼這個解決方案就會成爲以前的響應者提出的相同解決方案。

0

我相信Java 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); 
} 
相關問題