java中不太常見的東西(1) - 枚舉enum

引言

之所以我想總結一下java中不太用的東西,是因爲我再研究每個版本jdk中,發現有些內容“熱火朝天”,但是有些東西卻“門可羅雀”。比如說jdk1.5中新增了泛型,強化for循環和枚舉等,但是前兩者已經被各位熟知了,但是枚舉在日常開發中都不太會用的。在本篇博文中,我會詳細介紹enum的使用方式,同時比較常量與enum的優劣。筆者目前整理的一些blog針對面試都是超高頻出現的。大家可以點擊鏈接:http://blog.csdn.net/u012403290

技術點

在jdk1.5中新增的一個引用類型,用enum表示,用以表示一組固定常量,比如說用以標識訂單狀態:未付款,已付款,已發貨,已收貨等等。enum**沒有可以訪問的構造器(禁止使用public),枚舉類型是final的,因此枚舉類型是實例受控的,也就是安全的。同時,enum還支持添加任意的方法和域**、可以實現接口、而且還提供了Object類的所有方法(意思就是說enum默認繼承了Object,可以使用equals和toString的Object的方法)。

場景1-int/String枚舉模式

int/String枚舉模式是枚舉最簡單的應用。在電子商務系統中,必然存在着訂單狀態表示,假如說我用:1表示爲付款,2表示已付款。

那麼以下有三位程序員各自的實現:

程序員1直接用數值表示狀態:

package com.brickworkers;
/**
 * @author Brickworker
 * Date:2017年4月11日下午12:30:18 
 * 關於類EnumTest.java的描述:枚舉類例子
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class EnumTest {

    //程序員1直接用數值表示狀態
    //用戶支付成功後,修改訂單狀態
    order.setStatus(2);
}

這個是最噁心的,我稱這種代碼爲sick code,且不說別的程序員看不看得懂,我相信程序員1在過一段時間回來自己看都看不懂什麼意思。所以千萬別這麼做,在日常書寫代碼中要保證在類中不要出現直接的常量,不然你會被嘲笑的。

程序員2先用CommonConstant用以存儲狀態:

package com.brickworkers;

public class CommenConstant {

    public static final int NOT_PAY = 1;

    public static final int IS_PAY = 2;

}

然後再要使用的時候把靜態常量引入:

package com.brickworkers;
/**
 * @author Brickworker
 * Date:2017年4月11日下午12:30:18 
 * 關於類EnumTest.java的描述:枚舉類例子
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class EnumTest {

    //程序員2用靜態常量引入訂單狀態
    order.setStatus(CommenConstant.IS_PAY);
}

這個應該是我們實際開發中最常用的方式了,大家可以把所有的靜態變量整合到一起,每次用的時候引用一下。的確非常的便捷,博主說實話也是用這種方式,但是很明顯不夠優雅,個人覺得原因有如下3點:①所有的常量混在一個類中,這個類的功能只是爲了提供這些常量,這個類變成並沒有嚴格的意義;②各種常量混在一個類中,層次感和邏輯並不是非常清晰,你還要保證不能重名;③不能很好的打印出狀態,不能很直觀的看出什麼代表了什麼(1代表未付款)。

程序員3通過用枚舉類進行實現:

package com.brickworkers;

/**
 * @author Brickworker
 * Date:2017年4月11日下午12:53:32 
 * 關於類OrderStatus.java的描述:枚舉類
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public enum OrderStatus {

    NOT_PAY(1), IS_PAY(2);

    private final int val;
    private OrderStatus(int val) {
        this.val = val;
    }

    @Override
    public String toString() {
        return "名稱:"+this.name()+"  對應值:"+this.val;
    }
}

在這種表述中,用enum枚舉表示了未付款和已付款,同時它還重寫了Object的toString方法。那麼和上面一樣在使用的時候就是:

package com.brickworkers;
/**
 * @author Brickworker
 * Date:2017年4月11日下午12:30:18 
 * 關於類EnumTest.java的描述:枚舉類例子
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class EnumTest {


    //程序員3用枚舉來實現靜態常量
    order.setStatus(OrderStatus.IS_PAY.val);

}

或許如此看來,很多小夥伴覺得很臃腫,何必這麼麻煩?是的,因爲這裏一定要用int類型來表示訂單狀態,即使用了枚舉也必須轉化到int爲止,但是如果你一開始設計的時候就用枚舉來表示的話,那麼就可以避免這麼麻煩了,在上述的例子中就可以不用用構造函數對每個枚舉常量進行處理,也不需要用val這個成員變量。大家都是表示訂單狀態,用int和用String其實沒什麼很大的差別

同時,枚舉可以直接通過toString的重寫來打印狀態,比如說下面的代碼:

package com.brickworkers;
/**
 * @author Brickworker
 * Date:2017年4月11日下午12:30:18 
 * 關於類EnumTest.java的描述:枚舉類例子
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class EnumTest {
    public static void main(String[] args) {
        for (OrderStatus os :OrderStatus.values()) {
            System.out.println(os);
        }
    }
}

//打印結果:
//名稱:NOT_PAY  對應值:1
//名稱:IS_PAY  對應值:2

是不是有種煥然一新的感覺呢?

場景2 - 具有一定能力的枚舉

獲取在場景1中,枚舉並沒有給你帶來很大的吸引力。但是如果枚舉有一些更強大的功能呢?考慮第二種情況,比如說有這麼一種情況,比如說你給自己制定了一個學習生活計劃,週一到週三,這個時候就可以用枚舉類來實現了,而且非常適合。有兩種實現方式:

程序員用了enum獨特的switch-case來實現:

package com.brickworkers;

public enum MySchedule {
    MONDAY, TUESDAY, WEDNESDAY;

    String plan(){
        switch (this) {
        case MONDAY:
            return "玩";
        case TUESDAY:
            return "玩";
        case WEDNESDAY:
            return "玩";
        default:
            return "學習";
        }
    }

}

今天是週一,於是我去查看了我的日程安排表:

package com.brickworkers;
/**
 * @author Brickworker
 * Date:2017年4月11日下午12:30:18 
 * 關於類EnumTest.java的描述:枚舉類例子
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class EnumTest {

    public static void main(String[] args) {
        System.out.println("今天週一,我看了下我的安排,我發現我週一要做的是:"+MySchedule.MONDAY.plan());
    }
}

//輸出結果:
//今天週一,我看了下我的安排,我發現我週一要做的是:玩

是不是覺得enum也挺好玩的呢?仔細觀察上面的代碼,你會發現根本不會跑到default的語句塊,因爲枚舉沒有除卻週一,週二,週三之外的。那麼加入這個時候,我加入了週四呢?但是我忘記指定週四要幹嘛了,那麼不好意思,按照你的日常表來說,你沒有制定你新加入的枚舉常量,那麼你只能默認“學習”了。其實還有更好的實現方式,請參考下面的實現代碼:

package com.brickworkers;

public enum MySchedule {
    MONDAY {
        @Override
        String plan() {
            return "玩";
        }
    }, 
    TUESDAY {
        @Override
        String plan() {
            return "玩";
        }
    }, 
    WEDNESDAY {
        @Override
        String plan() {
            return "玩";
        }
    },

    THURSDAY {//新加入了一個星期四,被強制要求實現plan方法
        @Override
        String plan() {
            return "玩";
        }
    };

    abstract String plan();
}

把plan方法寫成抽象的,那麼你沒定義一個枚舉常量就會被強制要求你實現這個方法,這稱爲:特定於常量的方法實現。這個方式可以防止你新增一個常量而忘記添加它的特定屬性,enum會強制你去實現抽象的方法。比如說上面我新增了一個星期四,那麼我必須實現它的plan方法。 是不是覺得enum更有意思了呢?其實枚舉的功能可以按照你自己的想法進行擴展,比如說上面日常安排的枚舉,可以重寫toString來打印出一張完美的日程表。

場景3 - 枚舉裏面嵌套枚舉(策略枚舉)

類當中嵌套類,我們已經看過很多,在枚舉中我們可以實現嵌套枚舉。繼續上面的例子,爲了防止你往你的日程表中添加一天的時候忘記標註你要幹嘛,上面我們用abstract抽象方法解決了這個問題。在此基礎上,我們要構建一個更加優雅的方式,我們把要做的事情包裝成一個枚舉,然後把這個枚舉內嵌到枚舉內部(和靜態內部類功能的表示都很相似),接着構造一個構造函數,入參就是你要選擇安排的方式,具體的代碼實現如下:

package com.brickworkers;

public enum MySchedule {
    MONDAY(MyActivity.SLEEP), TUESDAY(MyActivity.SLEEP), WEDNESDAY(MyActivity.SLEEP);

    private MySchedule(MyActivity activity) {//建立一個機遇策略枚舉的一個構造函數,強制每個添加的日期都必須在活動列表中選擇一個活動
    }
    private enum MyActivity{//活動列表
        SLEEP {
            @Override
            String paly() {
                return "睡覺";
            }
        }, PLAY_BALL {
            @Override
            String paly() {
                return "打球";
            }
        }, STUDY {
            @Override
            String paly() {
                return "學習";
            }
        };
        abstract String paly();
    }

}

簡單解釋一下上面的代碼,內嵌的MyActivity表示你可以選擇的活動列表,在外部建立一個基於內嵌枚舉的構造函數,保證每次你往日程中添加一天就強制要求你去選擇一種活動。有的人會思考,我覺得這個方法與上面提供方法效果一樣啊,效果雖然差不多,但是內嵌枚舉這種模式,可以使得更容易讓人理解,並且你要的活動都給你包裝好了,你只要負責選擇就好。

尾記

枚舉就介紹這麼多,其實枚舉還有一些功能沒有介紹(實現接口,用接口組織枚舉等),可能我舉得例子沒有那麼足夠吸引你,但是希望在日常開發中多思考能否用枚舉來實現,比如說要建立用戶權限關係的時候(管理員,用戶,超級用戶,超級管理員),請考慮一下枚舉,或許你會發現新大陸。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章