[SPRING] Java Generics 및 Enum, 템플릿 매개 변수 손실
SPRINGJava Generics 및 Enum, 템플릿 매개 변수 손실
나는 꽤 복잡한 구조를 가지고 있으며 의도 한대로 작동하지 않습니다. 이것이 내가 한 일이다.
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
내가 뭘 잘못하고 있죠? 제네릭 매개 변수 유형을 유지하려면 어떻게해야합니까?
그 질문에 덧붙이고 싶습니다. 문제가 확인되지 않은 캐스팅 어딘가에 있다는 것을 알고 있습니다. 그러나 서비스는 다음과 같이 정의됩니다.
public interface ResultTypeOneService
extends ResultService<ResultTypeOne, Long, ResultTypeOneBO> {
}
그리고 왜 유추 된 것이 아닌지 나는 알지 못합니다.
편집 : 기술적으로, 명시 적으로 그들을 유추하면 작동합니다.
ResultTypes.RESULT_TYPE_ONE.<ResultTypeOne, Long, ResultTypeOneBO>getService().getRepository()
그러나 자동으로 수행되어야하며, 자동으로 작동하지 않는 이유는 무엇입니까? 형식을 포함하는 어떤 종류의 개체를 제공해야합니까? 왜 리턴 타입이 충분하지 않습니까?
EDIT2 : ResultTypeOne의 수퍼 클래스는 다음과 같습니다.
@SuppressWarnings("serial")
@EntityListeners(EntityListener.class)
@MappedSuperclass
public abstract class EntityBase implements Serializable {
그러나 경계의 어디에도 매핑되지 않습니다.
EDIT3 : @ Radiodef에 큰 감사! 이론적 인 해결책은 다음과 같이 끝나며 완벽하게 작동합니다.
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);
}
}
}
}
솔루션이 얼마나 오랜 시간 지속 되었기 때문에 열거 형 접근법을 고수하고 경계의 손실을 수용합니다. 이러한 경계를 강화하는 것보다 자신의 value () 구현을 추가해야하므로 더 많은 것을 잃게됩니다. 그러나 이것은 흥미로운 이론적 인 연습으로, 다시 도움을 주셔서 감사합니다.
해결법
-
==============================
1.좋아요, 먼저 당신이하는 일이 왜 당신이 생각하는 바가 아닌지 이해해야합니다. 좀 더 간단한 예를 살펴 보겠습니다.
좋아요, 먼저 당신이하는 일이 왜 당신이 생각하는 바가 아닌지 이해해야합니다. 좀 더 간단한 예를 살펴 보겠습니다.
interface Face { <T> List<T> get(); }
당신이 가지고있는 것은 일반적인 방법입니다. 제네릭 메서드의 형식 매개 변수는 호출 사이트에서 제공하는 내용에 따라 다릅니다. 예를 들면 다음과 같습니다.
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 ...; } }
이것은 당신이 할 수있는 일입니다 (지우개 때문에).하지만 그렇게해서는 안됩니다. 비 제너릭 코드와의 하위 호환성 만 허용됩니다. 경고를 듣고하지 말아야합니다. 그것을하는 것은 예를 들면 나는 아직도 따라 와서 다른 것을 돌려주기 위해 그것을 지시 할 수 있다는 것을 의미한다.
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에 대한 인수는 객체 참조의 유형에 따라 달라집니다.
Face<Number> f = ...; // get must return List<Number> List<Number> l = f.get();
우리는 그것을 구현할 수 있습니다.
class Impl implements Face<String> { @Override public List<String> get() { return ...; } }
또한 enum에서 공변 리턴 유형에 액세스 할 수 없습니다. 열거 형 상수에서 메서드를 재정의하면 해당 클래스는 익명입니다. 익명의 클래스는 이름이 없으므로 참조 할 수 없습니다. 따라서 프로그래머는이를 사용하기 위해 공변 리턴 유형을 알 수 없습니다. 또한 enum은 제네릭 형식 매개 변수를 선언 할 수 없습니다. 그래서 당신이하고 싶은 것은 단순히 enum으로 불가능합니다.
public static final 인스턴스가있는 클래스를 사용하여 일반 열거 형을 시뮬레이트 할 수 있습니다.
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 }; } }
그렇지 않으면 아이디어를 대폭 변경해야합니다.
-
==============================
2.어쩌면 열거 형 대신에 인터페이스 / 추상 클래스를 사용할 것인가?
어쩌면 열거 형 대신에 인터페이스 / 추상 클래스를 사용할 것인가?
열거 형은 매개 변수를 가질 수 없지만 클래스 및 인터페이스는 매개 변수를 가질 수 있습니다.
예를 들면 ...
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
열거 형은 아니지만 인터페이스가 있습니다. 봄이나 여기에서 "서비스"를 설정하는 메소드를 추가 할 수 있습니다 ...
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); ... } }
희망이 도움이 ...
-
==============================
3.당신은 당신이하고 싶은 것을 할 수 없습니다.
당신은 당신이하고 싶은 것을 할 수 없습니다.
런타임에
및 List 면 유형 삭제를 나열하십시오. 그리고 enum 매핑 된 getService () 함수도 마찬가지입니다.
generics에 대한 타입과 관련된 모든 것은 컴파일시에 유효성이 검사됩니다.
from https://stackoverflow.com/questions/28234960/java-generics-and-enum-loss-of-template-parameters by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] 매일 봄 cron 표현 1 : 01 : am (0) | 2018.12.10 |
---|---|
[SPRING] 스프링 보안에서 역할과 GrantedAuthority의 차이점 (0) | 2018.12.10 |
[SPRING] 봄 Java 구성 대 Jboss 7 (0) | 2018.12.10 |
[SPRING] org.apache.jasper.JasperException : 기본 네임 스페이스가 지정되지 않은 경우 접두사와 함께 함수 테스트를 사용해야합니다. (0) | 2018.12.10 |
[SPRING] 오라클에서 시퀀스의 값을 재설정해야합니다. (0) | 2018.12.10 |