2016-04-18 136 views
1

我是little library的作者,使用Mailgun service發送電子郵件。使用Java泛型設計轉換器SPI的正確方法

該庫目前有一個非常簡單的機制,可以使用某種HTML DSL生成豐富的郵件正文內容。我正在研究下一個版本,我希望提供一個轉換器SPI,以便您可以傳遞按需要格式化的對象。我正在考慮數字,日期,貨幣值等

我正在努力與泛型,通配符類型,範圍等。我認爲我的問題更多的是泛型設計的泛型使用的具體細節。所以我的問題是我應該如何設計我的SPI,因爲Java泛型的侷限性。

我已經開始定義轉換器接口:

public interface ContentConverter<T> { 
    String toString(T value); 
} 

有整個圖書館使用的配置對象。在那裏,用戶應該使用這種註冊方法來註冊該接口的實例。

public <T> Configuration registerConverter(ContentConverter<? super T> converter, 
              Class<T> classToConvert) 
{ 
    converters.add(new Converter<>(classToConvert, converter)); 
    return this; 
} 

Converter類是一個內部類encapsule轉換器和它的類。

private final static class Converter<T> { 
    private Class<T> classOfConverter; 
    private ContentConverter<? super T> contentConverter; 

    Converter(Class<T> classOfConverter, 
       ContentConverter<? super T> contentConverter) 
    { 
     this.classOfConverter = classOfConverter; 
     this.contentConverter = contentConverter; 
    } 
} 

Configuration保留轉換器列表。

private List<Converter<?>> converters = ... 

最後,該配置提供按需轉換器。

@SuppressWarnings("unchecked") 
public <T> ContentConverter<T> converter(Class<T> classToConvert) { 
    for (Converter<?> converter : converters) 
     if (classToConvert.isAssignableFrom(converter.classOfConverter)) 
      return (ContentConverter<T>) converter.contentConverter; 
    return (ContentConverter<T>) defaultConverter; 
} 

默認轉換器只是撥打Object.toString()

private final static ContentConverter<Object> defaultConverter = 
    new ContentConverter<Object>() { 
     @Override 
     public String toString(Object value) { 
      return value.toString(); 
     } 
    }; 

當用戶構建體消息,他可以調用任何這兩種方法來添加使用所配置的轉換器中的對象。

public Builder text(Object value) { 
    ContentConverter<?> converter = configuration.converter(value.getClass()); 
    return text(value, converter); 
} 

public <T> Builder text(T value, ContentConverter<T> converter) { 
    return text(converter.toString(value)); 
} 

public Builder text(String s) { 
    [...] // this is where the content is really appendend 
    return this; 
} 

但這不起作用。第一種方法不編譯。我嘗試過不同的組合,但我認爲問題更深入,關於類型的刪除。

回答

0

只需將第一個text方法的值參數設爲通用即可。 (您仍然需要的類對象施法):

public <T> Builder text(T value) { 
    ContentConverter<T> converter = configuration.converter((Class<T>)value.getClass()); 
    return text(value, converter); 
} 
+0

這不會編譯,因爲需要強制轉換爲值 – Jorge

+0

@Jorge值是T – wero

+0

您是對的,我沒有看到您改變參數類型。 – Jorge

0

確保你正在做正確的鑄件,也爲價值:

public <T> Builder text(Object value) { 
    Configuration configuration = new Configuration(); 
    ContentConverter<T> converter = configuration.converter((Class<T>)value.getClass()); 
    return text((T)value, converter); 
} 

而且在你的第一個定義T以及'文字'方法

無論如何,請注意,您的代碼將導致java.lang.StackOverflowError,因爲兩個text方法都在調用彼此並且永遠不會獲得任何輸出。

+0

有一個文本(字符串)重載的方法,我沒有在例子中包括(我編輯了問題)。它阻止了SO。 – sargue