使用泛型处理构造函数中的类型擦除

Cap*_*Man 10 java generics constructor type-erasure

我正在尝试创建一个只能容纳两个对象中的一个的类,我想用泛型来做这个.这是一个想法:

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...

}
Run Code Online (Sandbox Code Playgroud)

当然这不会起作用,因为由于类型擦除,构造函数具有相同的类型签名.我意识到一个解决方案是让一个构造函数同时使用两个,但我希望其中一个值为空,因此使用单个参数构造函数似乎更优雅.

// 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;
}
Run Code Online (Sandbox Code Playgroud)

有一个优雅的解决方案吗?


编辑1:我使用的是Java 6.

编辑2:我想要这样做的原因是因为我有一个方法可以返回两种类型中的一种.我做了一个没有泛型的具体版本,但想知道我是否可以使它通用.是的,我意识到拥有一种具有两种不同返回类型的方法是真正的问题,但我仍然很好奇是否有一个很好的方法来做到这一点.

我认为durron597的答案是最好的,因为它指出Union<Foo, Bar>并且Union<Bar, Foo>应该采取相同行动,但他们没有(这是我决定停止追求这一点的主要原因).这是一个比"丑陋"的构造函数更加丑陋的问题.

对于它的价值,我认为最好的选择可能是使这个抽象(因为接口不能指定可见性)并制作isAgetA东西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非常有趣.我还没有使用过它,但我Either在google搜索"java disjunct union"时发现了这一点,这正是我所寻找的.虽然功能Java仅适用于Java 7和8,但是幸运的是,我现在正致力于使用Java 8的项目.

dur*_*597 5

这样做真的没有任何意义.什么时候才有意义?例如(假设,目前,您的初始代码有效):

Union<String, Integer> union = new Union("Hello");
// stuff
if (union.isA()) { ...
Run Code Online (Sandbox Code Playgroud)

但如果你这样做,相反:

Union<Integer, String> union = new Union("Hello");
// stuff
if (union.isA()) { ...
Run Code Online (Sandbox Code Playgroud)

即使类和数据相同,这也会有不同的行为.你的概念isAisB基本上是"左右对比" - 更重要的是哪一个是左对比哪一个是一个字符串而哪一个是整数.换句话说,Union<String, Integer>是非常不同的Union<Integer, String>,这可能不是你想要的.

考虑如果我们举例说会发生什么,请说:

List<Union<?, ?>> myList;
for(Union<?, ?> element : myList) {
  if(element.isA()) {
    // What does this even mean? 
Run Code Online (Sandbox Code Playgroud)

事物是A无关紧要的事实,除非你关心它是左派还是右派,在这种情况下你应该称之为左派或右派.


如果这个讨论不是左派与右派,那么唯一重要的是在创建类时使用你的特定类型.简单地拥有一个接口会更有意义;

public interface Union<A, B> {
  boolean isA();
  boolean isB();
  A getA();
  B getB();
}
Run Code Online (Sandbox Code Playgroud)

您甚至可以在抽象类中执行"is"方法:

public abstract class AbstractUnion<A, B> {
  public boolean isA() { return getB() == null; }
  public boolean isB() { return getA() == null; }
}
Run Code Online (Sandbox Code Playgroud)

然后,当您实际实例化该类时,无论如何都将使用特定类型...

public UnionImpl extends AbstractUnion<String, Integer> {
  private String strValue;
  private int intValue

  public UnionImpl(String str) {
    this.strValue = str;
    this.intValue = null;
  }

  // etc.
}
Run Code Online (Sandbox Code Playgroud)

然后,当你真正选择了你的实现类型时,你实际上就会知道你得到了什么.


除此之外:如果在阅读完上述所有内容之后,您仍然希望按照您在初始问题中描述的方式执行此操作,正确的方法是使用私有构造函数的静态工厂方法,如@ JoseAntoniaDuraOlmos在此处所述.但是,我希望你能进一步思考你真正需要你的课程在一个真实的用例中做什么.