【发布时间】:2026-02-21 01:05:01
【问题描述】:
我正在尝试创建一个只能容纳两个对象之一的类,我想用泛型来做到这一点。思路如下:
public class Union<A, B> {
private final A a;
private final B b;
public Union(A a) {
this.a = a;
b = null;
}
public Union(B b) {
a = null;
this.b = b;
}
// isA, isB, getA, getB...
}
当然这不会起作用,因为由于类型擦除,构造函数具有相同的类型签名。我意识到一种解决方案是让一个构造函数同时使用两者,但我希望其中一个值为空,因此使用单参数构造函数似乎更优雅。
// Ugly solution
public Union(A a, B b) {
if (!(a == null ^ b == null)) {
throw new IllegalArgumentException("One must exist, one must be null!");
}
this.a = a;
this.b = b;
}
有没有优雅的解决方案?
编辑 1:我使用的是 Java 6。
编辑 2: 我想做这个的原因是因为我有一个可以返回两种类型之一的方法。我制作了一个没有泛型的具体版本,但想知道是否可以将其设为泛型。 是的,我意识到有一个具有两种不同返回类型的方法是手头的真正问题,但我仍然很好奇是否有一个好的方法来做到这一点。
我认为durron597's answer 是最好的,因为它指出
Union<Foo, Bar>和Union<Bar, Foo>应该采取相同的行动,但他们不会(这是我决定停止追求这一点的主要原因)。 这是一个比“丑陋”构造函数更丑陋的问题。对于它的价值,我认为最好的选择可能是使这个抽象(因为接口不能决定可见性)并制作
isA和getA的东西protected,然后在实现类中更好地命名避免<A, B>!=<B, A>问题的方法。我将添加我自己的答案并提供更多详细信息。
最终编辑:不管怎样,我决定使用静态方法作为伪构造函数(
public static Union<A, B> fromA(A a)和public static Union<A, B> fromB(B b))是最好的方法(同时将真正的构造函数设为私有)。Union<A, B>和Union<B, A>在仅用作返回值时,永远不会真正相互比较。
另一个编辑,6 个月后:当我问这个问题时,我真的不敢相信我是多么天真,静态工厂方法显然是绝对正确的选择,而且显然是不费吹灰之力。
除此之外,我发现Functional Java 非常有趣。我还没有使用它,但我在谷歌搜索'java disjunct union'时确实找到了这个
Either,这正是我想要的。缺点是,Functional Java 仅适用于 Java 7 和 8,但幸运的是我现在正在从事的项目使用 Java 8。
【问题讨论】:
-
您可以将“丑陋的解决方案”构造函数隐藏在工厂方法后面。
-
静态工厂通常是要走的路。但是,如果您有两个相互排斥的字段,那总是很可疑。
-
在我看来,这就像另一个
Eithermonad 的开始......所以你为什么不看看已经完成的大量实现。 -
是的,您需要不同的方法名称。构造函数无法获得的东西。不过,我看不出有什么大不了的。
-
@JoseAntonioDuraOlmos Reification 与静态类型无关,这是一个静态类型问题。另外,只有关于如何为 java 完成类型擦除的细节才会导致类型擦除成为问题。静态 FP 语言也可以进行类型擦除,但不会受此影响。
标签: java generics constructor type-erasure