写点什么

netty 系列之: 我有一个可扩展的 Enum 你要不要看一下?

作者:程序那些事
  • 2022 年 5 月 11 日
  • 本文字数:2580 字

    阅读完需:约 8 分钟

netty系列之:我有一个可扩展的Enum你要不要看一下?

简介

很多人都用过 java 中的枚举,枚举是 JAVA 1.5 中引用的一个新的类型,用来表示可以列举的范围,但是可能很少有人知道 java 中的 enum 到底是怎么工作的,enum 和 Enum 有什么关系?Enum 可不可以扩展?


一起来看看吧。

enum 和 Enum

JAVA1.5 中引入了枚举类,我们通常使用 enum 关键字来定义一个枚举类:


public enum StatusEnum {    START(1,"start"),    INPROCESS(2,"inprocess"),    END(3,"end");
private int code; private String desc;
StatusEnum(int code, String desc){ this.code=code; this.desc=desc; }}
复制代码


上面的枚举类中,我们自定义了构造函数,并且定义了 3 个枚举对象。


接下来看下怎么来使用这个枚举类:


    public static void main(String[] args) {        StatusEnum start = START;        System.out.println(start.name());        System.out.println(start.ordinal());        System.out.println(start.code);        System.out.println(start.desc);    }
复制代码


可以输出 code 和 desc 很好理解,因为这是我们自定义的枚举类中的属性,但是 name 和 ordinal 是什么呢?他们是哪里来的呢?


这里就要介绍 java.lang.Enum 类了,它是 JAVA 中所有 enum 枚举类的父类,name()和 ordinal()方法就是在这个类中定义的:


public final int ordinal() {        return ordinal;    }
public final String name() { return name; }
复制代码


其中 ordinal 表示的是枚举类中枚举的位置,那么就是枚举类中枚举的名字。在上面的例子中,START 的两个值分别是 1 和 START。


我们来看下 Enum 类的定义:


public abstract class Enum<E extends Enum<E>>        implements Comparable<E>, Serializable
复制代码


输入它是一个抽象类,但是编译器是不允许你继承这个类的。如果你强行继承,则会抛错:


Classes cannot directly extend 'java.lang.Enum'
复制代码


所以说,强扭的瓜不甜,大家一定要记住。


事实上,不仅仅 Enum 类本身不能被继承,上面创建的 enum 类 StatusEnum 也是不能被继承的。


这会造成一个什么问题呢?


如果这个 enum 是包含在一个外部 jar 包中的时候,你就没法对该 enum 进行扩展,在某些特定的情况下,这样的限制可能会带来一些不便。


还好,netty 也意识到了这个问题,接下来,我们看下 netty 是怎么解决的。

netty 中可扩展的 Enum:ConstantPool

netty 中的表示常量的类叫做 Constant,它有两个属性,分别是 ID 和 name:


public interface Constant<T extends Constant<T>> extends Comparable<T> {
int id();
String name();}
复制代码


存储这些 Constant 的就叫做 ConstantPool。ConstantPool 中有一个 ConcurrentMap 用来保存具体的 Constant。 我们看一下 ConstantPool 的工厂类方法 valueOf:


public T valueOf(String name) {        return getOrCreate(checkNonEmpty(name, "name"));    }
复制代码


valueOf 方法传入创建的 Constant 的名字。然后调用 getOrCreate 方法来创建新的 Constant:


    private T getOrCreate(String name) {        T constant = constants.get(name);        if (constant == null) {            final T tempConstant = newConstant(nextId(), name);            constant = constants.putIfAbsent(name, tempConstant);            if (constant == null) {                return tempConstant;            }        }
return constant; }
复制代码


可以看到 getOrCreate 就是向 constants Map 中创建和获取新创建的 constant 对象。

使用 ConstantPool

ConstantPool 是一个抽象类,如果我们需要新建一个枚举类池,可以直接继承 ConstantPool,然后实现其中的 newConstant 方法。下面是一个使用的具体例子:


public final class Foo extends AbstractConstant<Foo> {  Foo(int id, String name) {    super(id, name);  }}
public final class MyConstants {
private static final ConstantPool<Foo> pool = new ConstantPool<Foo>() { @Override protected Foo newConstant(int id, String name) { return new Foo(id, name); } };
public static Foo valueOf(String name) { return pool.valueOf(name); }
public static final Foo A = valueOf("A"); public static final Foo B = valueOf("B");}
private final class YourConstants { public static final Foo C = MyConstants.valueOf("C"); public static final Foo D = MyConstants.valueOf("D");}
复制代码


在上面的例子中,我们创建的枚举类继承自 AbstractConstant,然后自定义了 ConstantPool,从 pool 中可以返回新创建的 Foo 对象。


实时上,在 netty channel 中经常使用的 ChannelOption 就是 AbstractConstant 的子类,我们简单来看下其中的实现:


public class ChannelOption<T> extends AbstractConstant<ChannelOption<T>> {
private static final ConstantPool<ChannelOption<Object>> pool = new ConstantPool<ChannelOption<Object>>() { @Override protected ChannelOption<Object> newConstant(int id, String name) { return new ChannelOption<Object>(id, name); } }; public static <T> ChannelOption<T> valueOf(String name) { return (ChannelOption<T>) pool.valueOf(name); } public static <T> ChannelOption<T> valueOf(Class<?> firstNameComponent, String secondNameComponent) { return (ChannelOption<T>) pool.valueOf(firstNameComponent, secondNameComponent); } public static boolean exists(String name) { return pool.exists(name); } public static <T> ChannelOption<T> newInstance(String name) { return (ChannelOption<T>) pool.newInstance(name); }
复制代码


可以看到,ChannelOption 中定义了 ConstantPool,然后通过 ConstantPool 的 valueOf 和 newInstance 方法来创建新的 ChannelOption 对象。

总结

如果你也想要对枚举类进行扩展,不妨使用 Constant 和 ConstantPool 试试。


本文的例子可以参考:learn-netty4


更多内容请参考 http://www.flydean.com/49-netty-extensible-enum/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 2022 年 05 月 11 日阅读数: 17
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
netty系列之:我有一个可扩展的Enum你要不要看一下?_Java_程序那些事_InfoQ写作社区