第三十二条 用EnumSet代替位域

在java中,我们经常会定义一些常量值,来表示一些状态,每个常量值代表的意思都不一样,有的还是可以共存的


public class Text {
    public static final byte STYLE_BOLD          = 1<<0; // 1
    public static final byte STYLE_ITALIC        = 1<<1; // 2
    public static final byte STYLE_UNDERLINE     = 1<<2; // 4
    public static final byte STYLE_STRIKETHROUGH = 1<<3; // 8

    public void applyStyles(int styles) { }
}

我们有时候会需要用到OR位运算把几个常量合并到一个集合中,称作位域。 用法 new Text().applyStyles(Text.STYLE_BOLD | Text.STYLE_ITALIC); 
猛一看,可能有点懵。为什么要这么写,我举个最简单的例子。大家都做过多选题或不定项选择题,比如有abcd四个选项,需要统计到底需要哪个值,在上述例子中,STYLE_BOLD 代表 a,STYLE_ITALIC 代表 b, STYLE_UNDERLINE 代表 c, STYLE_STRIKETHROUGH 代表 d。 假如有个题的答案是 ab,那么我们就需要 int value = Text.STYLE_BOLD | Text.STYLE_ITALIC; 来表示;如果是acd, 那么就是 int value = Text.STYLE_BOLD | Text.STYLE_UNDERLINE | Text.STYLE_STRIKETHROUGH; 可能有疑问,问什么可以用这样方法标识值呢,为什么非得是 1、2、4、8 呢,我直接用0、1、2、3 来标识可以吗? 我们知道,位运算 是把int值转换为二进制,然后计算的, int value = Text.STYLE_BOLD | Text.STYLE_ITALIC 值为 1+2=3, int value = Text.STYLE_BOLD |Text.STYLE_UNDERLINE | Text.STYLE_STRIKETHROUGH; 值为1+4+8=13;通过二进制可以发现,他们的值的组成都是单一的, 例如 3 只能 是 1 和 2 组成, 13只能是1和4和8组成,只要能看懂二进制都能明白这个道理,不懂的需要先熟悉一下二进制。二进制本身就是以2为基础,每次都乘以2获取值位移,所以这种情况用位移能标识值的唯一的分解法。 如果 我们选取了 0、1、2、3 来标识abcd这四个值,那么不定项选择中,如果答案为bc,那么值是 1+2=3,但问题来了,d选项的值也是3,那么3对应的就是两种可能,bc或者d,因为是不定项选择,我们也没法确定是哪个,所以不能随意标识值,只能选择那些组合出来的值只能被分解成唯一的几个子值,位移就是其中一个完美的选择。饶了这么多,知识想解释一下上面那个方法的使用场景,有使用场景利于加深理解和记忆。

上述的方法中,我们会调用applyStyles(int styles)方法, 比如 new Text().applyStyles(Text.STYLE_BOLD | Text.STYLE_ITALIC); 我们需要在 applyStyles(int styles) 方法中,通过 & 运算,分解出每个值,然后做相应的操作,我写一下这个代码

    public final byte[] arr = {STYLE_BOLD, STYLE_ITALIC, STYLE_UNDERLINE, STYLE_STRIKETHROUGH};

    public void applyStyles(int styles) {
        for (int i = 0, count = arr.length; i < count; i++){
            int value = arr[i] & styles;
            if(value != 0){
                doSome(value);
            }
        }
    }

    private void doSome(int value) {
    System.out.println(value);
    }        
这样,我们在doSome里面就能打印想应的值了,说明上面也把值给分解了。这种是一个方法,但每次都去定义值,然后去分解,我们可以用枚举的手段来替换这种方法。EnumSet 就登场了,它是实现了Set接口,类型更安全。我们可以用枚举来代替 1、2、4、8 值,因为枚举本身就是唯一的。

    public enum Style{
        BOLD, ITALIC, UNDERLINE, STRIKETHROUGH;
    }

    public void applyStyles(Set<InviteShareCodeView.Text.Style> styles) {
        for(InviteShareCodeView.Text.Style style : styles){
            doSome(style);
        }
    }

    private void doSome(Style value) {
        System.out.println(value);
    }

调用方法为 new Text().applyStyles(EnumSet.of(Text.Style.BOLD, Text.Style.ITALIC)); 这样,就更清晰了,连位移都省了。我们 注意这个方法 EnumSet.of(Text.Style.BOLD,Text.Style.ITALIC);我们是用 EnumSet 把 两个枚举对象合并到集合里的,我们看看它的源码, EnumSet<E extends Enum<E>> extends AbstractSet<E> ,说明 EnumSet 是 Set 的子类,of(E e)方法是个重载类,里面可以传n个参数,of(E first, E... rest)  这个方法就是可以传n个参数,n 大于等于1, 从这些方法来看,EnumSet 和 普通 Set集合一样,都是可以装许多元素的,

    public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2) {
        EnumSet<E> result = noneOf(e1.getDeclaringClass());
        result.add(e1);
        result.add(e2);
        return result;
    }
先创建一个EnumSet对象,然后用add()方法添加元素,我们就把它看成一个集合来用就可以了。在java中,这个针对枚举的集合,还是很好用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值