如何强制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( ExtendedDiagram , ExtendedNode , ExtendedEdge和(这里出现问题) 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与上述限制?
感谢您坚持与我,任何帮助,非常感谢!
您可以使用:
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,这是一个相当大的问题。 通常情况下,如果你不能再理解括号内的内容,那么你的需求就太远了 – 你不能指望用户比你更了解它。
无论如何,为了让你的代码编译,你可以尝试在Shape
types中定义这样的东西:
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使您能够在广泛的对象上使用方法,而不是使用接口。 你最后进入一个通用的必须是一种对象。