在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中,这个针对枚举的集合,还是很好用的。