如何强制genericstypes参数成为一个接口?

java中有一种方法来指定generics类的types参数必须是一个接口(不仅仅是扩展它!)

我想要做的是以下几点:

public class MyClass<X extends SomeInterface, Y extends SomeOtherClass & X> 

这意味着Y必须是SomeOtherClass的子类并实现X.我目前得到的编译器是

typesX不是一个接口; 它不能被指定为有界参数

那么,我怎样才能告诉编译器X必须是一个接口?

编辑:
好的,我想我过分简化了我的问题。 让我们使用我的实际应用领域来更加清楚:

我有一个表示图的API。 一个包含节点边缘对象。 所有这三个类都实现了Shape接口。 形状可能具有子形状,父形状并属于图表。

问题是,我需要制作这个API的两个版本:一个只有基本function的开放源代码,另一个带有更多function的扩展源代码。 但是,扩展API只能提供返回扩展types( ExtendedDiagramExtendedNodeExtendedEdge和(这里出现问题) ExtendedShape )的方法。
所以我有这样的东西:

 /* BASIC CLASSES */ public interface Shape<X extends Shape<X,Y>, Y extends Diagram<X,Y>>{ public List<X> getChildShapes(); public X getParent(); public Y getDiagram(); ... } public class Diagram<X extends Shape<X,Y>, Y extends Diagram<X,Y>> implements Shape<X,Y>{...} public class Edge<X extends Shape<X,Y>, Y extends Diagram<X,Y>> implements Shape<X,Y>{...} ... /* EXTENDED CLASSES */ public interface ExtendedShape extends Shape<ExtendedShape,ExtendedDiagram> { ... } public class ExtendedDiagram extends Diagram<ExtendedShape,ExtenedDiagram> implements ExtendedShape { ... } public class ExtendedEdge extends Edge<ExtendedShape,ExtenedDiagram> implements ExtendedShape { ... } ... 

扩展的API工作正常,基本的API代码给出了一些警告,但是在使用基本API时会出现主要问题:

 public class SomeImporter<X extends Shape<X,Y>, Y extends Diagram<X,Y>, E extends Edge<X,Y>>{ private Y diagram; public void addNewEdge(E newEdge){ diagram.addChildShape(newEdge); ... 

最后一行给我以下警告:

参数(E)不适用于Diagramtypes的addChildShape(X)

所以现在我只想指出E也需要实现X,所有的都会很好 – 我希望;)

这一切是有道理的吗? 你们知道一个办法吗? 还是有更好的方式来获得扩展的API与上述限制?
感谢您坚持与我,任何帮助,非常感谢!

6 Solutions collect form web for “如何强制genericstypes参数成为一个接口?”

您可以使用:

 class Foo<T extends Number & Comparable> {...} 

具有一个types参数的类Foo,T.Foo必须实例化为Number的子types,并实现Comparable。

在generics上下文中, <Type extends IInterface>处理extends和implements。 这是一个例子:

 public class GenericsTest<S extends Runnable> { public static void main(String[] args) { GenericsTest<GT> t = new GenericsTest<GT>(); GenericsTest<GT2> t2 = new GenericsTest<GT>(); } } class GT implements Runnable{ public void run() { } } class GT2 { } 

GenericsTest将接受GT,因为它实现了Runnable。 GT2不会,因此在尝试编译第二个GenericsTest实例化时会失败。

也许你可以简化你的模型:太多的generics在可读性方面很快就成为一个真正的痛苦,如果你定义一个公共API,这是一个相当大的问题。 通常情况下,如果你不能再理解括号内的内容,那么你的需求就太远了 – 你不能指望用户比你更了解它。

无论如何,为了让你的代码编译,你可以尝试在Shapetypes中定义这样的东西:

 public <S extends Shape<?,?>> void addChildShape(S shape); 

这应该做到这一点。

HTH

你写了以下内容:

 public interface Shape<X extends Shape<X,Y>, Y extends Diagram<X,Y>>{ public List<X> getChildShapes(); public X getParent(); public Y getDiagram(); ... } 

我会build议,至less,摆脱X型variables,如下所示:

 public interface Shape<Y>{ public List<Shape<Y>> getChildShapes(); public Shape<Y> getParent(); public Diagram<Y> getDiagram(); ... } 

原因在于你最初写的东西会受到types参数的无限recursion嵌套的影响。 一个形状可以嵌套在父形状中,可以嵌套在另一个形状中,所有这些形状都必须在types签名中考虑…不是一个好的可读性配方。 那么,在你的例子中,这种情况不会发生,在这种情况下,你声明“Shape <X>”而不是“Shape <Shape <X >>”,但是这是你要进入的方向,如果你想要实际上使用自己的形状…

我也可能会build议更进一步,摆脱Yvariables类似的原因。 Javagenerics不能很好地处理这种组合。 当试图通过generics为这种types的build模强制执行静态types时,我发现types系统开始在以后开始扩展时崩溃。

这是典型的动物/狗问题…动物有一个getChildren(),但狗的孩子也必须是狗… Java不能应付这个好,因为(部分原因是缺乏抽象types,如在语言像Scala一样,但我并不是说你应该急于使用Scala来处理这个问题),typesvariables必须在各种不属于他们的地方开始声明。

使用预处理器来生成代码的“简化”版本。 使用apt和annotations可能是一个很好的方法。

我可能会在这里基地,但我对generics的理解是有点不同。

如果我错了,我正在问一个人纠正我。

IMO –

这是一个非常混乱的结构,你有。 你有无限的引用Shape的子类,它看起来像。

你的Shape接口和HashMap的使用方式是一样的,但是我从来没有见过一个HashMap做你正在做的事情,最终你必须让X是Shape中的一个类。 否则你正在做HashMap

如果你总是希望X与接口“是一对一”的关系,那就不会发生。 这不是generics。 generics用于将方法应用于多个对象,而接口不能是对象。 接口定义了客户和类之间的契约。 你所能做的就是说你将接受任何实现了Runnable的对象,因为你的所有或者一些方法都需要使用Runnable接口方法。 否则,如果你没有指定和你定义为,那么你的类与客户端之间的契约可能会产生意外的行为,并导致错误的返回值或抛出exception。

例如:

 public interface Animal { void eat(); void speak(); } public interface Dog extends Animal { void scratch(); void sniff(); } public interface Cat extends Animal { void sleep(); void stretch(); } public GoldenRetriever implements Dog { public GoldenRetriever() { } void eat() { System.out.println("I like my Kibbles!"); } void speak() { System.out.println("Rufff!"); } void scratch() { System.out.println("I hate this collar."); } void sniff() { System.out.println("Ummmm?"); } } public Tiger implements Cat { public Tiger() { } void eat() { System.out.println("This meat is tasty."); } void speak() { System.out.println("Roar!"); } void sleep() { System.out.println("Yawn."); } void stretch() { System.out.println("Mmmmmm."); } } 

现在,如果你做了这个课,你可以期望你总是可以调用'speak()'和'sniff()'

 public class Kingdom<X extends Dog> { public Kingdom(X dog) { dog.toString(); dog.speak(); dog.sniff(); } } 

但是,如果你这样做,你不能总是打电话'说()'和'嗅()“

 public class Kingdom<X> { public Kingdom(X object) { object.toString(); object.speak(); object.sniff(); } } 

结论:

generics使您能够在广泛的对象上使用方法,而不是使用接口。 你最后进入一个通用的必须是一种对象。

  • 将数组投射到IEnumerable <T>
  • ArrayList.toArray()中的Javagenerics
  • 如何找出每个对象在ArrayList <Object>中的types?
  • 最简洁的方式将ListBox.items转换为通用列表
  • 为什么我不能使用float值作为模板参数?
  • 在部分特定的类上标记调度和静态方法
  • generics结构的构造函数中的“期望types参数”错误
  • 通用类的文件名约定
  • 为什么C#编译器允许在IEnumerable <T>和TAlmostAnything之间进行显式转换?
  • 我可以创build一个genericstypes的字典吗?
  • 如何解决运算符'!='不能应用于'T'和'T'types的操作数