Java特性组合的通用方案

简介: Java特性组合的通用方案


一、背景
一些框架的特性组合,以及开发中业务的某个字段是多个特征组合,如果直接用数字,组合较多保存非常复杂。

这里提供一个参考工具类, 大家感兴趣可以参考改造。

二、源码
特征

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文件去读读字节码可以收获更多。

创作不易,如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。

相关文章
|
2月前
|
存储 安全 Java
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
80 2
|
2月前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
48 3
|
2月前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
38 2
|
2月前
|
存储 算法 Java
Java Set因其“无重复”特性在集合框架中独树一帜
【10月更文挑战第14天】Java Set因其“无重复”特性在集合框架中独树一帜。本文深入解析Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定的数据结构(哈希表、红黑树)确保元素唯一性,并提供最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的`hashCode()`与`equals()`方法。
38 3
|
2月前
|
安全 Java API
Java 17新特性让你的代码起飞!
【10月更文挑战第4天】自Java 8发布以来,Java语言经历了多次重大更新,每一次都引入了令人兴奋的新特性,极大地提升了开发效率和代码质量。本文将带你从Java 8一路走到Java 17,探索那些能让你的代码起飞的关键特性。
98 1
|
4天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
57 2
|
17天前
|
存储 Java 开发者
什么是java的Compact Strings特性,什么情况下使用
Java 9引入了紧凑字符串特性,优化了字符串的内存使用。它通过将字符串从UTF-16字符数组改为字节数组存储,根据内容选择更节省内存的编码方式,通常能节省10%至15%的内存。
|
26天前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
44 6
|
1月前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
35 4
|
2月前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
101 3