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>
,但最后它变成了Object
和Serializable
。
我究竟做错了什么? 我怎样才能保留generics参数types?
我想补充一点,是的,我确实意识到这个问题是在一个没有检查的情况下进行的。 但是这些服务被定义为
public interface ResultTypeOneService extends ResultService<ResultTypeOne, Long, ResultTypeOneBO> { }
而且我不知道为什么这些types没有被推断。
编辑 :从技术上讲,如果我明确推断他们:
ResultTypes.RESULT_TYPE_ONE.<ResultTypeOne, Long, ResultTypeOneBO>getService().getRepository()
但它应该是自动的,为什么它不能自动工作? 我应该提供一些包含types的对象吗? 为什么返回types不够呢?
EDIT2 : ResultTypeOne
的超类
@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。
- 只从Spring MVC 3控制器返回string消息
- 如何实例化一个在Spring框架中使用generics的对象?
- 我可以在哪里下载Spring Framework jars而不使用Maven?
- Spring XML文件configuration层次结构的帮助/解释
- Spring的GA,RC和M2版本有什么区别?
- 无法parsingSpring属性占位符
- 为什么我会使用Java / Spring的Scala / Lift?
- Spring注解@Repository和@Service
- Spring 3.1 entityManagerFactory java.lang.NoSuchFieldError:NULL错误