前言
在讀取Excel檔案資料,有時候不可避免地需要把獲取到的字串轉型為基本型別的物件。以前都是自己寫轉換,難度也不大。後來聽說,有可以直接用的輪子——Apache 的commons-beanutils這個包,有提供ConvertUtils。以下我的相關記錄。
我要的異常呢?
聽說有可以用的輪子,第一反應,肯定是拿來先跑幾個測試案例。這東西使用起來也很簡單,引數就是源字串和目的型別。我就先測了一下,一個亂碼String轉Boolean,看看會不會丟擲異常。結果出乎意外的是,它居然沒有丟擲異常,還返回了false。認真看了一下,才發現它直接把異常列印出來了,還返回了預設值。
程式碼如下:
public class ConvertTest { public static void main(String[] args) { Object result; result = ConvertUtils.convert("@7##jF*&%#$", Boolean.class); System.out.println(result); } }
輸出結果:
真是夠嗆,你不丟擲異常,我留你何用。我就是要你丟擲異常,然後我再在上層決定怎麼和使用者互動,你倒好,直接列印出來了,還給我個預設值,那怎麼知道原來的值到底是錯誤的還是bool false。
於是我想,這種工具都有一個尿性——可配置。我就想肯定有什麼方法,比如xxxwithException(),或者throwException()這樣的設定。結果翻了一下,還真沒有。哇,上頭。
核心——ConvertUtilsBean
剛好有時間,於是就看它到底怎麼實現的。看了才知道,原來它是把工作委託給ConvertUtilsBean來做的。
public static Object convert(final String value, final Class<?> clazz) { //這裡獲取一個ConvertUtilsBean的例項來執行convert方法 return ConvertUtilsBean.getInstance().convert(value, clazz); }
於是順藤摸瓜,我就隨便翻了翻ConvertUtilsBean。我的眼睛就盯著,看看有沒有exception這個關鍵字,果然讓我找到了!!!
public void register(final boolean throwException, final boolean defaultNull, final int defaultArraySize) { registerPrimitives(throwException); registerStandard(throwException, defaultNull); registerOther(throwException); registerArrays(throwException, defaultArraySize); }
這裡可以配置是否要丟擲異常。於是我就用這個bean,測了一下,果然可以。
public class ConvertBeanTest { public static void main(String[] args) { ConvertUtilsBean convertUtilsBean = new ConvertUtilsBean(); convertUtilsBean.register(true, false, 0); Object obj; obj = convertUtilsBean.convert("@7##jF*&%#$", Boolean.class); System.out.println(obj); } }
輸出:
很好,終於丟擲異常了。那麼,我用這個ConvertUtilsBean就可以了。
ConvertUtilsBean為何能夠想轉什麼就轉什麼?
實際上,它也不是想轉什麼就轉什麼。初始條件下,它內部只註冊了基本型別的轉換器。
public ConvertUtilsBean() { converters.setFast(false); //這個方法是關鍵,它清除當前所有轉換器,並重新初始化 deregister(); converters.setFast(true); }
public void deregister() { converters.clear(); //false參數列示,是否丟擲異常。即預設不丟擲異常。 //這裡註冊了基本型別的轉換器 registerPrimitives(false); registerStandard(false, false); registerOther(true); registerArrays(false, 0); register(BigDecimal.class, new BigDecimalConverter()); register(BigInteger.class, new BigIntegerConverter()); }
註冊是什麼概念?
“註冊”這個詞,看上去挺玄乎的,其實一般就是寫到一個登錄檔裡面,然後需要的時候從表中檢索。在這個實現中,登錄檔,不過就是一張HashMap。而註冊操作,就是把Converter物件放到這個雜湊表中。
/** * The set of {@link Converter}s that can be used to convert Strings * into objects of a specified Class, keyed by the destination Class. */ private final WeakFastHashMap<Class<?>, Converter> converters = new WeakFastHashMap<Class<?>, Converter>();
我們可以看到,這個表的Key是型別。也就是說,我們在使用convert方法的時候,已經指定了key,自然就找到了對應的Converter。那我們還能想到什麼呢,那就是“覆蓋”。因為HashMap中,Key是唯一的,所以同種型別的Converter只能存在一個,即新註冊的Converter會覆蓋同型別的Converter。
對了,忘記提了,這個包有一個Converter介面,如果要自定義的話,也可以自己實現相關的類,註冊到這個Bean上,然後統一使用這個Bean。
service provider framework
之所以想到這個,是因為前幾天剛剛開始看《Effective Java》這本書中,而裡面說的service provider framwork的結構,和這個非常類似。
三個要素:
service介面 => Converter介面
register API => register方法
access API => ConverterUtils工具類
體現的思想就是:客戶端和實現類解耦,參照上面的,客戶端只要知道ConvertUtils或者ConvertUtilsBean這個類就好了,不需要去記該用哪個Converter。