一、背景
一些框架的特性组合,以及开发中业务的某个字段是多个特征组合,如果直接用数字,组合较多保存非常复杂。
这里提供一个参考工具类, 大家感兴趣可以参考改造。
二、源码
特征
public interface Feature {
/**
* 获取特性(掩码)
*/
int getMask();
/**
* 所有特性
*/
Feature[] listAll();
}
特征工具类
public class FeatureUtil {
/**
* 此特性是否开启
*
* @param features 特性值
* @param feature 某个特性
* @return 是否开启
*/
public static boolean isEnabled(int features, Feature feature) {
return (features & feature.getMask()) != 0;
}
/**
* 配置某个特性
*
* @param features 特性值
* @param feature 某个特性
* @param state 是否开启
* @return
*/
public static int config(int features, Feature feature, boolean state) {
if (state) {
features |= feature.getMask();
} else {
features &= ~feature.getMask();
}
return features;
}
/**
* 开启某些特性的值
*
* @param features 特性数组
* @return 特性值
*/
public static int of(Feature... features) {
if (features == null) {
return 0;
}
return of(Stream.of(features).collect(Collectors.toSet()));
}
/**
* 开启某些特性的值
*
* @param features 特性数组
* @return 特性值
*/
public static int of(Set<Feature> features) {
if (features == null) {
return 0;
}
int value = 0;
for (Feature feature : features) {
value |= feature.getMask();
}
return value;
}
/**
* 判断特性值包含哪些特性
*
* @param features 特性值
* @param featureArray 特性数组
* @return 包含的特性
*/
public static Set<Feature> resolve(Integer features, Feature[] featureArray) {
if (featureArray == null) {
throw new IllegalArgumentException("特征数组不为null");
}
if (features == null || features == 0) {
return new HashSet<>(0);
}
Set<Feature> featureSet = new HashSet<>();
for (Feature feature : featureArray) {
if ((features & feature.getMask()) != 0) {
featureSet.add(feature);
}
}
return featureSet;
}
}
枚举举例
public enum DemoEnum implements Feature {
FIRST("第一个"),
SECOND("第二个"),
THIRD("第三个");
DemoEnum() {
mask = (1 << ordinal());
}
DemoEnum(String desc) {
mask = (1 << ordinal());
this.desc = desc;
}
private final int mask;
private String desc;
@Override
public final int getMask() {
return mask;
}
@Override
public Feature[] listAll() {
return DemoEnum.values();
}
public String getDesc() {
return desc;
}
}
测试类
public class FeatureUtilTest {
private int features;
@Before
public void init() {
features = FeatureUtil.of(DemoEnum.values());
}
@Test
public void isEnabled() {
boolean enabledFirst = FeatureUtil.isEnabled(features, DemoEnum.FIRST);
boolean enabledSecond = FeatureUtil.isEnabled(features, DemoEnum.SECOND);
Assert.assertTrue(enabledFirst);
Assert.assertTrue(enabledSecond);
}
@Test
public void config() {
int defaultFeature = 0;
int enableFirst = FeatureUtil.config(defaultFeature, DemoEnum.FIRST, true);
int enableFirst2 = FeatureUtil.of(DemoEnum.FIRST);
Assert.assertEquals(enableFirst, enableFirst2);
}
@Test
public void of() {
// 前两个特性
int two = FeatureUtil.of(DemoEnum.FIRST, DemoEnum.SECOND);
Assert.assertEquals(two, 3);
// 后两个特性
int lastTwo = FeatureUtil.of(DemoEnum.THIRD, DemoEnum.SECOND);
Assert.assertEquals(lastTwo, 6);
}
@Test
public void resolve() {
int features = FeatureUtil.of(DemoEnum.FIRST, DemoEnum.THIRD);
Set<Feature> resolve = FeatureUtil.resolve(features, DemoEnum.values());
Assert.assertEquals(resolve.size(), 2);
Assert.assertTrue(resolve.contains(DemoEnum.FIRST));
Assert.assertTrue(resolve.contains(DemoEnum.THIRD));
}
}
三、解析
java.lang.Enum#ordinal 函数继承自枚举对象,获取该枚举值在枚举类中的排序,第一个为0,往后依次是1,2...。
这样不同的枚举通过左移进行区分,不同的特征组合通过 不同特征之间的&运算即可区分。
如特征1 + 特征4 则,两个特征的掩码 按位或即可。
如果取消某个特性,总特性值&=~某个性,让某个特性位置为0即可。
怎么证明隐式继承了那个类呢?
方法1:单元测试
@Test
public void testExtends(){
Assert.assertTrue(DemoEnum.FIRST instanceof Enum);
}
方法2:源码注释法
Enum类的注释第一句话就是:
This is the common base class of all Java language enumeration types.
这是Java语言中枚举类型的基类。
方法3:反解析
随手写一个枚举类
public enum SomeEnum {
}
javac SomeEnum.java
javap -verbose SomeEnum.class
Compiled from "SomeEnum.java"
public final class com.chujianyun.common.enums.SomeEnum extends java.lang.Enum<com.chujianyun.common.enums.SomeEnum>
...
很明显可以看出继承自java.lang.Enum类。
另外我们还有意外发现,该类自动加上了final修饰符,因此无法再继承。
四、总结
由于枚举隐式继承了Enum类,由于Java不支持多ji'cheng因此如果想实现统一的方法,只能通过实现接口方式。
位运算的恰当使用可以将一些复杂逻辑简单化,可以非常容易得应对变化。
我们学习的时候,写代码的时候多去源码里看看,会有一些意外收获。
有时间研究下Java反解析class文件去读读字节码可以收获更多。
创作不易,如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。