【API知識】類型轉換工具ConvertUtils引發的思考


前言

  在讀取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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM