Java的枚举:两个枚举类型,每个都包含引用对方?(Java Enums: Two enum typ

2019-07-20 12:02发布

有没有办法来解决造成具有参考两个彼此枚举类加载的问题?

我有两套枚举,Foo和Bar,像这样定义的:

public class EnumTest {

  public enum Foo {
    A(Bar.Alpha),
    B(Bar.Delta),
    C(Bar.Alpha);

    private Foo(Bar b) {
      this.b = b;
    }

    public final Bar b;
  }

  public enum Bar {
    Alpha(Foo.A),
    Beta(Foo.C),
    Delta(Foo.C);

    private Bar(Foo f) {
      this.f = f;
    }

    public final Foo f;
  }

  public static void main (String[] args) {
    for (Foo f: Foo.values()) {
      System.out.println(f + " bar " + f.b);
    }
    for (Bar b: Bar.values()) {
      System.out.println(b + " foo " + b.f);
    }
  }
}

以上代码生成作为输出:

A bar Alpha
B bar Delta
C bar Alpha
Alpha foo null
Beta foo null
Delta foo null

我明白为什么会发生 - 在JVM启动类加载富; 它看到Bar.Alpha在Foo.A的构造函数,所以它启动类加载吧。 它认为在调用Bar.Alpha的构造函数中Foo.A参考,但(因为我们仍然在Foo.A的构造函数)Foo.A在这一点空,所以Bar.Alpha对象的构造函数传递空。 如果我扭转两个for循环(或其他参考美孚之前吧),输出的变化,使酒吧的价值观是正确的,但Foo的价值都没有。

有没有什么办法来解决这个问题? 我知道我可以创建一个静态映射,并在第三类中的静态地图,但感觉还算的hackish给我。 我也可以做Foo.getBar()和Bar.getFoo()方法引用外部地图,所以它甚至不改变我的接口(实际的课程我使用视察员,而不是公共领域),但它仍然感觉那种不洁给我。

(我在我的实际系统这样做的原因是:Foo和Bar代表类型的消息2个应用程序发送给对方;在Foo.b和Bar.f字段表示给定消息的预期响应类型 - 所以在我示例代码,当APP_1接收Foo.A,它需要与Bar.Alpha反之亦然答辩。)

提前致谢!

Answer 1:

一个最好的方法是使用枚举多态性技术

public class EnumTest {
    public enum Foo {
        A {

            @Override
            public Bar getBar() {
                return Bar.Alpha;
            }
        },
        B {

            @Override
            public Bar getBar() {
                return Bar.Delta;
            }
        },
        C {

            @Override
            public Bar getBar() {
                return Bar.Alpha;
            }
        },

        ;

        public abstract Bar getBar();
    }

    public enum Bar {
        Alpha {

            @Override
            public Foo getFoo() {
                return Foo.A;
            }
        },
        Beta {

            @Override
            public Foo getFoo() {
                return Foo.C;
            }
        },
        Delta {

            @Override
            public Foo getFoo() {
                return Foo.C;
            }
        },

        ;

        public abstract Foo getFoo();
    }

    public static void main(String[] args) {
        for (Foo f : Foo.values()) {
            System.out.println(f + " bar " + f.getBar());
        }
        for (Bar b : Bar.values()) {
            System.out.println(b + " foo " + b.getFoo());
        }
    }
}

以上代码生成你想要的输出:

A bar Alpha
B bar Delta
C bar Alpha
Alpha foo A
Beta foo C
Delta foo C

也可以看看:

  • 具体枚举的Overridding方法


Answer 2:

这个问题与其说是“两个枚举相互引用”,它更“两个枚举在它们的构造相互引用”。 这个循环引用是棘手的部分。

如何使用Foo.setResponse(Bar b)Bar.setResponse(Foo f)的方法呢? 而不是在富构造设置美孚酒吧(类似一个酒吧在酒吧构造美孚),你就使用初始化的方法? 例如:

富:

public enum Foo {
  A, B, C;

  private void setResponse(Bar b) {
    this.b = b;
  }

  private Bar b;

  public Bar getB() {
    return b;
  }

  static {
    A.setResponse(Bar.Alpha);
    B.setResponse(Bar.Delta);
    C.setResponse(Bar.Alpha);
  }
}

酒吧:

public enum Bar {
  Alpha, Beta, Delta;

  private void setResponse(Foo f) {
    this.f = f;
  }

  private Foo f;

  public Foo getF() {
    return f;
  }

  static {
    Alpha.setResponse(Foo.A);
    Beta.setResponse(Foo.C);
    Delta.setResponse(Foo.C);
  }
}

另外,你提到Foo和酒吧有两种类型的消息。 是否可以将它们合并成一个单一的类型? 从我所看到的,在这里他们的行为是一样的。 这不固定循环逻辑,但它可能给你一些其他的洞察您的设计...



Answer 3:

因为它似乎你将是反正硬编码,为什么不能有这样的事

public static Bar responseBar(Foo f) {
 switch(f) {
  case A: return Bar.Alpha;
  // ... etc
 }
}

每个枚举? 它看起来像你必须在你的榜样一些重叠的反应,所以你甚至可以采取通过下降的情况下的优势。

编辑:

我喜欢EnumMap的汤姆的建议; 我表现可能更快的EnumMap的,但似乎是某种有效的Java描述优雅的建筑并没有被这个特殊的问题被给予-然而,以上所提供的交换机解决方案将构建两个静态EnumMaps一个很好的方式,那么反应可能是这样的:

 public static Bar response(Foo f) { return FooToBar.get(f); }
 public static Foo response(Bar b) { return BarToFoo.get(b); }


Answer 4:

有趣的设计。 我看你的需要,但什么是你打算怎么办时要求稍微移动,从而使响应Foo.Epsilon,APP_1应该发送无论是Bar.Gamma或Bar.Whatsit?

你认为和丢弃hackish的解决方案(把关系到地图)似乎给你更多的灵活性,并且避免你的循环引用。 它也保持划分责任:消息类型本身不应该是负责了解他们的反应,他们应该?



Answer 5:

您可以使用EnumMap的,和枚举的一个内填充它。

private static EnumMap<Foo, LinkedList<Bar>> enumAMap;

public static void main(String[] args) throws Exception {
    enumAMap = new EnumMap<Foo, LinkedList<Bar>>(Foo.class);
    System.out.println(Bar.values().length); // initialize enums, prevents NPE
    for (Foo a : Foo.values()) {
        for (Bar b : enumAMap.get(a)) {
            System.out.println(a + " -> " + b);
        }
    }
}

public enum Foo {
    Foo1(1),
    Foo2(2);

    private int num;

    private Foo(int num) {
        this.num = num;
    }

    public int getNum() {
        return num;
    }
}

public enum Bar {
    Bar1(1, Foo.Foo1),
    Bar2(2, Foo.Foo1),
    Bar3(3, Foo.Foo2),
    Bar4(4, Foo.Foo2);

    private int num;
    private Foo foo;

    private Bar(int num, Foo foo) {
        this.num = num;
        this.foo = foo;
        if (!enumAMap.containsKey(foo)) {
            enumAMap.put(foo, new LinkedList<Bar>());
        }
        enumAMap.get(foo).addLast(this);
    }

    public int getNum() {
        return num;
    }

    public Foo getFoo() {
        return foo;
    }
}

输出:

4
Foo1 -> Bar1
Foo1 -> Bar2
Foo2 -> Bar3
Foo2 -> Bar4


文章来源: Java Enums: Two enum types, each containing references to each other?