一、背景
一些架構的特性組合,以及開發中業務的某個字段是多個特征組合,如果直接用數字,組合較多儲存非常複雜。
這裡提供一個參考工具類, 大家感興趣可以參考改造。
二、源碼
特征
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檔案去讀讀位元組碼可以收獲更多。
創作不易,如果覺得本文對你有幫助,歡迎點贊,歡迎關注我,如果有補充歡迎評論交流,我将努力創作更多更好的文章。