Javagenerics和枚举,模板参数的丢失

我有一个相当复杂的结构,并没有按预期工作。 这就是我所做的:

public interface ResultServiceHolder { <M, ID extends Serializable, BO extends BusinessObject<M, ID>> ResultService<M, ID, BO> getService(); } public enum ResultTypes implements ResultServiceHolder { RESULT_TYPE_ONE { @Override public ResultOneService getService() { //unchecked conversion? return serviceInitializer.getResultOneService(); } }, RESULT_TYPE_TWO { @Override public ResultTwoService getService() { //unchecked conversion? return serviceInitializer.getResultTwoService(); } }, RESULT_TYPE_THREE { @Override public ResultThreeService getService() { //unchecked conversion? return serviceInitializer.getResultThreeService(); } }; protected ServiceInitializer serviceInitializer; protected void setServiceInitializer(ServiceInitializer serviceInitializer) { this.serviceInitializer = serviceInitializer; } @Component public static class ServiceInitializer { @Autowired private ResultOneService resultOneService; @Autowired private ResultTwoService resultTwoService; @Autowired private ResultThreeService resultThreeService; @PostConstruct public void init() { for(ResultTypes resultType : ResultTypes.values()) { resultType.setServiceInitializer(this); } } //getters } } 

目的是基于枚举概括这个调用,而只是能够迭代枚举数组。

  for(ResultServiceHolder resultServiceHolder : ResultTypes.values()) { if(resultServiceHolder.equals(post.getPostResultTypeCode())) { return resultServiceHolder.getService().createResultSearchCriteriaResponse(postId); } } 

这工作得很好,很好。 但是,如果我说

 ResultTypes.RESULT_TYPE_ONE.getService().getRepository() 

然后它是一个BaseRepository<Object, Serializable>而不是BaseRepository<ResultTypeOne, Long> 。 方法resultTypeHolder.getService()返回ResultService<M, ID, BO> ,但最后它变成了ObjectSerializable

我究竟做错了什么? 我怎样才能保留generics参数types?

我想补充一点,是的,我确实意识到这个问题是在一个没有检查的情况下进行的。 但是这些服务被定义为

 public interface ResultTypeOneService extends ResultService<ResultTypeOne, Long, ResultTypeOneBO> { } 

而且我不知道为什么这些types没有被推断。

编辑 :从技术上讲,如果我明确推断他们:

 ResultTypes.RESULT_TYPE_ONE.<ResultTypeOne, Long, ResultTypeOneBO>getService().getRepository() 

但它应该是自动的,为什么它不能自动工作? 我应该提供一些包含types的对象吗? 为什么返回types不够呢?

EDIT2ResultTypeOne的超类

 @SuppressWarnings("serial") @EntityListeners(EntityListener.class) @MappedSuperclass public abstract class EntityBase implements Serializable { 

但它并没有映射到任何地方。

编辑3 :非常感谢@Radiodef! 理论上的解决scheme结束了以下,并将工作得很好:

 public interface ResultServiceHolder<M, ID extends Serializable, BO extends BusinessObject<M, ID>> { ResultService<M, ID, BO> getService(); } public abstract class ResultTypes<M, ID extends Serializable, BO extends BusinessObject<M, ID>> implements ResultServiceHolder<M, ID, BO> { public static ResultTypes<?, ?, ?>[] values() { return new ResultTypes<?, ?, ?>[] {RESULT_ONE, RESULT_TWO, RESULT_THREE}; } public static final ResultTypes<ResultOne, Long, ResultOneBO> RESULT_ONE = new ResultTypes<ResultOne, Long, ResultOneBO>("Result One") { @Override public ResultOneService getService() { return serviceInitializer.resultOneService; } }; public static final ResultTypes<ResultTwo, Long, ResultTwoBO> RESULT_TWO = new ResultTypes<ResultTwo, Long, ResultTwoBO>("Result Two") { @Override public ResultTwoService getService() { return serviceInitializer.resultTwoService; } }; public static final ResultTypes<ResultThree, Long, ResultThreeBO> RESULT_THREE = new ResultTypes<ResultThree, Long, ResultThreeBO>("Result Three") { @Override public ResultThreeService getService() { return serviceInitializer.resultThreeService; } }; protected String name; protected ServiceInitializer serviceInitializer; private ResultTypes(String name) { this.name = name; } protected void setServiceInitializer(ServiceInitializer serviceInitializer) { this.serviceInitializer = serviceInitializer; } @Component static class ServiceInitializer { @Autowired private ResultOneService resultOneService; @Autowired private ResultTwoService resultTwoService; @Autowired private ResultThreeService resultThreeService; @PostConstruct public void init() { for (ResultTypes resultType : ResultTypes.values()) { resultType.setServiceInitializer(this); } } } } 

我认为,由于解决scheme有多长时间,我会坚持使用enum方法,并接受这种限制。 我不得不增加自己的values()实现,而不是通过强制执行这些边界来获得更多。 不过,这是一个有趣的理论练习,再次感谢您的帮助。

好的,首先你需要明白为什么你所做的可能不是你所想的。 我们来看一个更简单的例子。

 interface Face { <T> List<T> get(); } 

你在那里有一个通用的方法, get 。 通用方法的types参数取决于调用站点提供的内容。 所以例如像这样:

 Face f = ...; // this call site dictates T to be Number List<Number> l = f.<Number>get(); 

当你重写它像

 class Impl implements Face { @Override public List<String> get() { return ...; } } 

这是你能做的事情 (只是因为擦除),但你可能不应该这样做 。 只允许向后兼容非generics代码。 你应该听取警告,不要这样做。 这样做意味着例如我仍然可以前来指示它返回其他的东西:

 Face f = new Impl(); // now I've caused heap pollution because you // actually returned to me a List<String> List<Number> l = f.<Number>get(); 

这就是为什么有一个未经检查的转换。

你可能的意思是使用一个通用的接口声明:

 interface Face<T> { List<T> get(); } 

现在T的参数取决于对象引用的types。

 Face<Number> f = ...; // get must return List<Number> List<Number> l = f.get(); 

我们可以像这样实现它

 class Impl implements Face<String> { @Override public List<String> get() { return ...; } } 

此外,您不能访问枚举的协变返回types。 当你覆盖枚举常量的方法时,它的类是匿名的。 一个匿名类没有名字,不能被引用。 因此程序员不能知道它的协变返回types来使用它。 此外,枚举不能声明genericstypes参数。 所以你想要做什么是根本不可能与枚举。

您可以使用具有public static final实例的类来模拟generics枚举:

 public abstract class SimEnum<T> implements Face<T> { public static final SimEnum<Number> A = new SimEnum<Number>() { @Override public List<Number> get() { return ...; } }; public static final SimEnum<String> B = new SimEnum<String>() { @Override public List<String> get() { return ...; } }; private SimEnum() {} public static SumEnum<?>[] values() { return new SimEnum<?>[] { A, B }; } } 

否则,你需要彻底改变你的想法。

也许使用接口/抽象类而不是枚举?

枚举不能有types参数,但类和接口可以。

例如…

接口

Entity.java

“东西”界面…

 import java.io.Serializable; public interface Entity<K extends Serializable> { // TODO: Put entity type things here! // for example, things like "K getId();" // You may want an abstract base class for this interface that all Entitys extend } 

Repository.java

CRUD东西的东西…

 import java.io.Serializable; public interface Repository<K extends Serializable, V extends Entity<K>> { V getValue(K key); // Other CRUD stuff } 

Service.java

一个服务负责与东西做东西…

 public interface Service<K, V> { // Could have an abstract service class that has a repository and implements this for you... V get(K key); // Other "generic service" type stuff } 

固体类

Entity1.java

带有String键的固定基类

 public class Entity1 implements Entity<String> { // TODO implement Entity stuff... } 

Entity2.java

带有Integer键的固定基类

 public class Entity2 implements Entity<Integer> { // TODO implement methods... } 

Entity1Service.java

固体实体1服务

 public class Entity1Service implements Service<String, Entity1> { // Would not have to implement this if you extended an abstract base Service class @Override public Entity1 get(String key) { return null; } } 

Entity2Service.java

固体实体2服务

 public class Entity2Service implements Service<Integer, Entity2> { // Wouldn't need this if you had abstract Service class either... @Override public Entity2 get(Integer key) { return null; } } 

ServiceHolder.java

不是一个枚举,而是一个接口 – 你可以添加方法来设置从“spring”或在这里的“服务”…

 import java.io.Serializable; public abstract class ServiceHolder<K extends Serializable, V, S extends Service<K, V>> { public static final ServiceHolder<String, Entity1, Entity1Service> ENTITY_1_SERVICE = new ServiceHolder<String, Entity1, Entity1Service>() {}; public static final ServiceHolder<Integer, Entity2, Entity2Service> ENTITY_2_SERVICE = new ServiceHolder<Integer, Entity2, Entity2Service>() {}; private S service; private ServiceHolder() { } public S getService() { return service; } public void setService(S service) { this.service = service; } } 

有趣的一点

我想这是你想要的东西,请让我知道,如果我误解了…

 public class PleaseCompile { public static void main(String[] args) { Entity1 solid1 = ServiceHolder.ENTITY_1_SERVICE.getService().get("[KEY]"); Entity2 solid2 = ServiceHolder.ENTITY_2_SERVICE.getService().get(42); ... } } 

希望这可以帮助…

你不能做你想做的事情。

在运行时List<String>List<Integer>脸型擦除。

你的枚举映射getService()函数也是如此。

与genericstypes相关的所有东西都在编译时进行validation。