通用限制地狱:绑定不匹配

时间:2011-01-20 18:32:23

标签: java generics restriction type-erasure

我正在开发一个具有广泛的泛型继承和依赖树的项目。转到编辑以查看更好的示例。 基础知识看起来像这样:

class A {
  ...
}

class B {
  ...
}

class C extends B {
  ...
}

class D<T extends B> extends A {
  ...
}

class StringMap<T extends A> {
   HashMap<String, T> _elements;
   ...
}

所以现在我要编写一个包含特定StringMap类型的类。

class X {
  StringMap<D<C>> _thing = new StringMap<D<C>>;
  ... 
}

到目前为止,一切正常。 D<C>实际上是一个非常长的名称,并且特定组合将在代码的其他部分中非常频繁地出现,因此我决定使用特定组合的类,以便更清楚并且具有更短的名称。

class DC extends D<C> {

}

//and go to update X
class X {
  StringMap<D<C>> _thing = new StringMap<D<C>>(); //still works fine
  StringMap<DC> _thing = new StringMap<DC>(); //error
  ... 
}

<击>

Eclipse给出错误

  

绑定不匹配:类型DC不能替代<T extends A>类型的有界参数StringMap<T>

所以问题是,为什么这不仅仅起作用? DC除了扩展D<C>并且回显构造函数之外什么都不做。为什么StringMap看到DC只是一个不同于它的儿童类?

修改
好的,重新设计的例子更接近我实际做的事情。我测试了它确实产生了错误。我在这里做的是使用泛型类型来确保clone()为继承树中实现它的人返回正确的类。然后在子类中,我使用B<T extends B<T>>来确保B的子类在B的子类中作为泛型类型T传递。

public abstract class Undoable<T> implements Comparable<T> {
  public abstract T clone();
  public abstract void updateFields(T modified);
}

abstract public class A<T extends A<T, U>, U extends Comparable<U>>
    extends Undoable<T> {
  abstract U getKey();

  @Override
  public int compareTo(T element)
  {
    return getKey().compareTo(element.getKey());
  }
}

public class B<T extends B<T>> extends A<T, String> {
  @Override
  public T clone()
  {
    // TODO Auto-generated method stub
    return null;
  }

  @Override
  public void updateFields(T modified)
  {
    // TODO Auto-generated method stub
  }

  @Override
  String getKey()
  {
    // TODO Auto-generated method stub
    return null;
  }
}

public class C extends B<C> {

}

public class D<T extends B<T>> extends A<D<T>, String> {
  @Override
  String getKey()
  {
    // TODO Auto-generated method stub
    return null;
  }

  @Override
  public D<T> clone()
  {
    // TODO Auto-generated method stub
    return null;
  }

  @Override
  public void updateFields(D<T> modified)
  {
    // TODO Auto-generated method stub
  }
}

public class DC extends D<C> {

}

public class StringMap<T extends Undoable<T>> {
  HashMap<String, T> _elements;

}

public class Main {
  public static void main(String[] args)
  {
    StringMap<D<C>> _thing = new StringMap<D<C>>(); //works
    StringMap<DC> _thing1 = new StringMap<DC>(); //error
//Bound mismatch: The type DC is not a valid substitute for
//the bounded parameter <T extends Undoable<T>> of the type StringMap<T>

  }
}

4 个答案:

答案 0 :(得分:8)

你必须做错其他的事情,因为以下工作正常:

import java.util.HashMap;

public class Q {
    class A {
    }
    class B {
    }
    class C extends B {
    }
    class D<T extends B> extends A {
    }

    class StringMap<T extends A> {
        HashMap<String, T> _elements;
    }

    class DC extends D<C> {

    }

    //and go to update X
    class X {
        StringMap<D<C>> thing1 = new StringMap<D<C>>(); // still works fine
        StringMap<DC> thing2 = new StringMap<DC>(); // NO error!!!
    }
}

尝试发布这样一个类来重现你的错误。

答案 1 :(得分:4)

如前所述,您的代码没问题,但如果我能猜到,您打算写下面一行,这确实会导致错误:

StringMap<D<C>> _thing = new StringMap<DC>; //error

原因与导致以下问题的原因相同:

ArrayList<Number> = new ArrayList<Integer>();

在定义标识符的类型时给予类的泛型类型参数,即在左值,不能实例化,即在rvalue中,由参数继承的类型左值中给出的参数。如果参数不同,那么这些类型不会被认为是兼容的,即使它们应该是直观的(如果在语言中实现泛型有点不同)。

这有点儿了......

答案 2 :(得分:2)

我不确定你为什么要这样做以及你想要使用StringMap的确切原因,但是将StringMap定义改为此将允许你完成编译所做的事情:

class StringMap<T extends Undoable<? super T>> {
  HashMap<String, T> _elements;
}

这意味着类型T必须是任何类型的Undoable,只要该类型是T的逆变量(实际上是T本身)。所以现在你可以这样做:

StringMap<DC> _thing1 = new StringMap<DC>(); // no more error
_thing1._elements.put("a key", new DC());

话虽如此,这只是一个理论上的练习 - 我强烈建议您避免使用这种复杂的继承层次结构,但如果没有完整的用例,很难提出替代方案。

我希望有所帮助!

答案 3 :(得分:0)

嗯。我希望这不是古老的,但我正在解决我也遇到的问题。我注意到在D中, D必须扩展B C必须扩展D ,因为A中的循环,足以让我放入一堆努力没有人注意到。问题出在StringMap类中,它严格地希望自己的输入扩展泛型类,其泛型必须是它的输入。

D有效,因为D与扩展中提到的类相同。它是安全的。 D指自己。 D扩展A,String&gt;。用它做一个StringMap很好,因为D实现了Undoable&gt;因为D扩展了A,String&gt ;,它扩展了Undoable&gt;。另一方面,DC也必须实现Undoable,这是不可能的,因为它只能扩展D;一个解决方案可能是它还可以实现Undoable,其中需要使用DC的方法的新定义才能工作。这是您遇到的真正问题。我希望这能解决你的问题。

我的老问题:

在D类中,T延伸B,但是D延伸A>。 但是,它不是返回预期的A或A&gt;而是扩展A,String&gt;。 但是, D不会延伸B 。因此,A,String&gt;由于继承自两个具有不同通用封闭类的相同接口并未在Java中实现,因此T必须扩展D.但是, C不会扩展D ,这是你的问题遇到这是您将来可能需要解决的问题。