У меня возникли проблемы с моей структурой наследования для группы классов-оболочек с дженериками. Это в основном структура:
public abstract class SuperWrapper<E extends Super>{
private E wrappedObject;
public E getWrappedObject(){
return wrappedObject;
}
}
public abstract class MiddleWrapper<E extends Middle> extends SuperWrapper<E>{}
public class SubAWrapper extends MiddleWrapper<SubA>{}
public class SubBWrapper extends MiddleWrapper<SubB>{}
Обернутые классы следуют одной и той же структуре наследования, поэтому в основном:
public class Super{}
public class Middle extends Super{}
public class SubA extends Middle{}
public class SubB extends Middle{}
Эти обернутые классы не мои и не могут быть изменены (что является причиной классов-оболочек). Эта структура очень хорошо работает для большинства целей, однако возникают некоторые проблемы, когда я хочу, чтобы метод возвращал либо SubAWrapper, либо SubBWrapper.
Это то, что я пробовал до сих пор:
public static MiddleWrapper<?> findMiddle(String id){
SubAWrapper a = new SubAWrapper();
SubBWrapper b = new SubBWrapper();
if(returnSubA){
return a;
} else{
return b;
}
}
Это компилируется и работает, но SonarQube предупреждает об использовании подстановочного знака в возвращаемом типе, и, судя по моим гуглениям, это не одно из предупреждений, которые следует игнорировать.
Другой путь:
public static <E extends Middle> MiddleWrapper<E> findMiddle(String id){
SubAWrapper a = new SubAWrapper();
SubBWrapper b = new SubBWrapper ();
if(returnSubA){
return (MiddleWrapper<E>) a;
} else{
return (MiddleWrapper<E>) b;
}
}
Это компилируется и работает с текущим кодом, НО это кажется опасным. Если вызывающая сторона использует этот метод следующим образом: MiddleWrapper<SubA> middle = findMiddle("1");
он будет скомпилирован нормально, но если findMiddle попытается вернуть SubBWrapper, во время выполнения возникнет исключение ClassCastException. То, что я хотел бы работать, но не было:
public static MiddleWrapper<Middle> findMiddle(String id){
SubAWrapper a = new SubAWrapper ();
SubBWrapper b = new SubBWrapper ();
if(returnSubA){
return (MiddleWrapper<Middle>) a; //Compile error here
} else{
return (MiddleWrapper<Middle>) b; //and here
}
}
Итак, мой вопрос в основном заключается в том, есть ли правильный способ написать метод findMiddle, который компилируется, запускается и следует лучшим практикам?
В качестве дополнительного кредита, есть ли способ написать метод, чтобы он возвращал список, содержащий как SubA, так и SubB. Вот так, но без подстановочного знака:
public static List<MiddleWrapper<?>> getAllMiddle(){
List<MiddleWrapper<?>> ret = Lists.newArrayList();
ret.add(new SubAWrapper());
ret.add(new SubBWrapper());
return ret;
}
пс. На данный момент мы все еще используем Java 6, но обновление до Java 8 запланировано на следующий год, поэтому решения, использующие функциональность Java 7-8, в конечном итоге все равно будут полезны.