Java 使用无集合的泛型时出现类型擦除错误

Java 使用无集合的泛型时出现类型擦除错误,java,Java,我想用Java实现monad。 使用下面的代码,我得到了错误方法other(V)的擦除与类型other中的另一个方法相同 我看不出t和V是如何被擦除为相同的类型,从而导致错误的 public class Either<T, V> { private T t; private V v; public Either(T t) { this.t = t; } public Either(V v) { this.v

我想用Java实现monad。 使用下面的代码,我得到了错误
方法other(V)的擦除与类型other中的另一个方法相同
我看不出t和V是如何被擦除为相同的类型,从而导致错误的

public class Either<T, V> {
    private T t;
    private V v;

    public Either(T t) {
        this.t = t;
    }

    public Either(V v) {
        this.v = v;
    }
}
公共类{
私人T;
私人V V;
公共或(T){
t=t;
}
公共关系(V){
这个,v=v;
}
}
相关帖子,但不重复:

下面的文章介绍了两个构造函数,一个是
Collection
,另一个是
Collection
。问题是构造函数的两个参数都将被类型擦除到
集合
,因此它们具有相同的类型签名

我的文章不是重复的,因为主要类型不同

下面的post地址方法,一个使用
compareTo(Real r)
,另一个使用
compareTo(Object o)

我的帖子不是重复的,因为类型
T
V
没有关联,至少在我看来没有关联

问题

  • 为什么会发生这种类型的擦除错误

  • 如何解决此错误以在Java中实现任意一个monad


显然,这是与相同的问题,只是我的问题带有泛型的味道

无法在运行时解析泛型类型,因为它们已转换为对象。 因此,
public-other(T)
->
public-other(Object-T)
和具有相同类型签名的
public-other(V V)
->
public-other(Object V)

要解决此问题,可以执行以下操作:

public class Either<T, V> {
    private T t;
    private V v;

    private Either(T t, V v) {
        this.t = t;
        this.v = v;
    }

    public static Either instaceOfT(T t) {
        return new Either<>(t, null);
    }

    public static Either instaceOfV(V v) {
        return new Either<>(null, v);
    }
}
公共类{
私人T;
私人V V;
二等兵(T,V){
t=t;
这个,v=v;
}
公共静态或instaceOfT(T){
返回新的(t,null);
}
公共静态或instaceOfV(V){
返回新的(null,v);
}
}
如果您可以在泛型类型上使用,这将克服擦除问题。尽管如此,我知道在大多数情况下,您不希望限制这样一个类,而是以一种非常通用的方式定义它,这样它就可以在任何地方重用

public class Either<T extends String, V extends Object> {

private T t;
private V v;

private Either(T t, V v) {
    this.t = t;
    this.v = v;
}

public static <T extends String, V extends Object> Either<T, V> either(T t) {
    return new Either<T, V>(t, null);
}

public static <T extends String, V extends Object> Either<T, V> either(V v) {
    return new Either<T, V>(null, v);
}

}
公共类{
私人T;
私人V V;
二等兵(T,V){
t=t;
这个,v=v;
}
公共静态任选一(T){
返回新的(t,null);
}
公共静态或任意(V){
返回新的(null,v);
}
}

您可以在每个构造函数中引入不同类型的第二个参数,Java可以:

public class Either<T, V> {
    public enum Left { Left }
    public enum Right { Right }
    private T t;
    private V v;

    public Either(T t, Left l) {
        this.t = t;
    }

    public Either(V v, Right r) {
        this.v = v;
    }
}
公共类{
公共枚举左{Left}
公共枚举权限{Right}
私人T;
私人V V;
公共或公共(T,左l){
t=t;
}
公共或(V,右r){
这个,v=v;
}
}
这是合理的,因为现在第二个参数可以用作鉴别器

让我印象深刻的是发现Java完全能够推断调用哪个构造函数,即使使用以下代码:

    new Either<Integer,Exception>(new Integer(2),null);
    new Either<Integer,Exception>(new Exception(),null);
new或(新整数(2),null);
新的(新异常(),null);
那么,为什么它对擦除方法(V)和类型(other)中的另一种方法(other)如此挑剔呢?我不知道

我所知道的是,我不喜欢总是向我的构造函数传递一个额外的无用参数。因此,我求助于使用Java varargs的解决方案:

public class Either<T, V> {
    public enum Left { Left }
    public enum Right { Right }
    private T t;
    private V v;

    public Either(T t, Left... l) {
        this.t = t;
    }

    public Either(V v, Right... r) {
        this.v = v;
    }
}
公共类{
公共枚举左{Left}
公共枚举权限{Right}
私人T;
私人V V;
公共或(T,左…l){
t=t;
}
公共或(V,右…r){
这个,v=v;
}
}
当然,现在可以这样调用构造函数:

    new Either<Integer,Exception>(new Integer(2),null);
    new Either<Integer,Exception>(new Exception(),null,Right,nul);
    new Either<Integer,Exception>(new Integer(2));
    new Either<Integer,Exception>(2);
    new Either<Integer,Exception>(new Exception());
new或(新整数(2),null);
新的(新异常(),null,Right,nul);
只要我不必阅读你的代码,我就不会反对。
但好处是您现在可以像这样构建任意一个实例:

    new Either<Integer,Exception>(new Integer(2),null);
    new Either<Integer,Exception>(new Exception(),null,Right,nul);
    new Either<Integer,Exception>(new Integer(2));
    new Either<Integer,Exception>(2);
    new Either<Integer,Exception>(new Exception());
new或(新整数(2));
新的(2);
新的或(新的异常());
您可以在以下位置找到此类型的完整、不可变的实现:

T和V都被类型擦除为
对象,因此编译时两个构造函数具有相同的类型签名

我们可以通过遵循有效Java的建议来避免这种情况:明智地使用重载,并且更喜欢静态工厂方法而不是公共构造函数

public class Either<T, V> {
    private T t;
    private V v;

    private Either(T t, V v) {
        this.t = t;
        this.v = v;
    }

    public static <L, R> Either<L, R> left(L leftValue) {
        return new Either(leftValue, null);
    }

    public static <L, R> Either<L, R> right(R rightValue) {
        return new Either(null, rightValue);
    }
}
公共类{
私人T;
私人V V;
二等兵(T,V){
t=t;
这个,v=v;
}
公共静态或左(L leftValue){
返回新的值(leftValue,null);
}
公共静态任意权利(R rightValue){
返回新的(null,rightValue);
}
}

具有相同数量参数的方法重载通常会导致歧义,尤其是在泛型中。通过避免使用这些方法并公开不同的命名方法,您可以让编译器和API用户感到高兴。

我看到这是一个自我回答的问题(很好:),但是OP在某个地方实际包含一个问题会很有帮助……其他人开发了这样一个类:。所以你可能想看看他们是如何实现的。你呃,忘了在那里分配你的
v
。我应该知道不要把代码直接写进So。很好。为了将来参考,这里提供了函数式Java的源代码: