2013-08-27 2 views
2

Допустим, у меня есть эти два иерархий наследования:Java параллельно иерархий наследования родовых типов и ковариант- возвращает

class EntityA { ... } 
class EntityB extends EntityA { ... } 

class EntityAPrime extends AbstractPrime<EntityA> { ... } 
class EntityBPrime extends EntityAPrime { ... } 

Я также хотел бы ковариантных типов возврата:

public class EntityA { 
    public EntityAPrime prime() { 
    return new EntityAPrime(); 
    } 
} 

public class EntityB extends EntityA { 
    @Override 
    public EntityBPrime prime() { 
    return new EntityBPrime(); 
    } 
} 

до сих пор так хорошо.

Проблема в том, что я хотел EntityBPrime, чтобы в конечном итоге расширить AbstractPrime<EntityB>, но так как он расширяет EntityAPrime это в конечном счете расширяет AbstractPrime<EntityA>.

я могу сделать EntityAPrime и EntityBPrime универсальна, но тогда я потеряю ковариантные возвращаемые типы:

public class EntityA { 
    public EntityAPrime<EntityA> prime() { 
    return new EntityAPrime(); 
    } 
} 

public class EntityB extends EntityA { 
    @Override 
    public EntityBPrime<B> prime() { // ERROR: EntityBPrime extends EntityAPrime 
    return new EntityBPrime();  // but can't substitute <EntityB> for <EntityA> 
    } 
} 

Все работает, если я возвращаю связанный подстановочные но есть disadvantages, связанные с этим, и я не смог бы назвать конкретный сеттер на AbstractPrime.

Еще одна идея, которую я имел, состоит в том, чтобы сделать EntityA и EntityB самим общим.

public class EntityA<T extends EntityA> { 
    public EntityAPrime<T> prime() { ... } 
} 

public class EntityB<T extends EntityB> extends EntityA<T> { 
    @Override 
    public EntityBPrime<T> prime() { ... } 
} 

Это должно работать, но я думаю, что это будет запутаться (у нас есть более 100 объектов, которые могли бы использовать подобный рисунок).

Итак, есть ли способ:

  • держать общековариантные возвращается
  • не использовать дженерики на EntityA и EntityB
  • не prime() вернуть связанного WildCard
  • и имеют EntityBPrime в конечном счете продлить AbstractPrime<EntityB> вместо AbstractPrime<EntityA>

Примечание: простые классы - это сгенерированный код, но у меня есть контроль над кодом, который генерирует.

+1

Нет, если вы не нарушаете наследование между A и B. Можете ли вы использовать композицию вместо этого? –

+0

@EricStein Объекты были смоделированы с учетом наследования и сопоставлены с таблицами таким образом. Хотя это действительно хорошо. –

+0

Я думаю, что ты за рулем. Расширение снова наносит удар. :-( –

ответ

1

Во-первых, это немного запутывает то, что оба интерфейса и классы называются A и B. Итак, я переименовал классы как AImpl и BImpl соответственно. Кроме того, ваши классы APrime и BPrime не печатаются (они должны быть), поэтому я предположил, что они напечатаны на <T extends A> и <T extends B> соответственно (если я неправильно понял ваши намерения/цели/требования, извиняюсь).

private interface A {} 
private interface B extends A {} 
private abstract class AbstractPrime<T extends A>{} 
private class APrime<T extends A> extends AbstractPrime<T>{} 
private class BPrime<T extends B> extends APrime<T>{} 
private class AImpl {...} 
private class BImpl {...} 

Мой первый инстинкт был, как вы считали, для ввода реализаций на интерфейсах. Это будет работать:

private class AImpl<T extends A> { 
    public APrime<T> prime(){ 
    return new APrime<>(); 
    } 
} 

private class BImpl<T extends B> extends AImpl<T> { 
    public BPrime<T> prime(){ 
    return new BPrime<>(); 
    } 
} 

Однако, если вы набрали APrime на <T extends A> и BPrime на <T extends B>, вам нужно возвращаемый тип prime() набираться? Может ли следующее решение работать для вас?

class APrime<T extends A> extends AbstractPrime<T>{} 
class BPrime<T extends B> extends APrime<T>{} 

public class AImpl { 
    public APrime prime(){ 
    return new APrime<>(); 
    } 
} 

public class BImpl extends AImpl { 
    public BPrime prime(){ 
    return new BPrime<>(); 
    } 
} 

Я думаю, ответ на этот вопрос несколько предполагаете ли вы нуждаясь следующее:

private interface C extends B {} 

public void main(String[] args) { 
    BPrime<C> prime = new BImpl<C>().prime(); // do you need this? 
    BPrime<B> prime = new BImpl<>().prime(); // do you need this? 


    BPrime prime = new BImpl().prime(); or is this sufficient...? 
} 
+0

Во-первых, спасибо, что нашли время, чтобы помочь. , Я уточнил вопрос: нет интерфейсов, и я бы хотел избежать «AImpl ', где 'AImpl' является сущностью (см. Вопрос для получения более подробной информации об этом), хотя мы, вероятно, можем согласиться с тем, чтобы сущности генерировали общие звуки как и для исправления. Я также хотел бы избежать необработанных типов. Наконец, чтобы ответить на оба вопроса «вам это нужно?», мне не нужен первый, но мне нужен второй. Проблема в том, что я не может иметь его без создания «AImpl». –

1

Как я уже говорил в комментариях, есть много мест, которые печатают Params опущены, что делает вся история не выглядит правильной.

Во-первых, ваши Штрихи должны, по крайней мере, выглядит следующим образом:

class EntityAPrime<T extends EntityA> extends AbstractPrime<T> {} 

class EntityBPrime<T extends EntityB> extends EntityAPrime<T> {} 

Тогда вопрос приходит к вашей организации

class EntityA { 
    public EntityAPrime<????> prime(); <- What should be the reasonable type param here? 
} 

class EntityB extends EntityA { 
    public EntityBPrime<????> prime(); <- Same here 
} 

Ответ зависит от вашего дизайна.

Я не вижу, как ваш второй выбор грязнее, чем ваш оригинал (учитывая оригинал один реален грязный)

Вы можете считать это (Не тестировался, и является ли он или нет право в значительной степени зависит от ваш дизайн, но, по крайней мере, тип PARAMS выглядит разумным для меня):

public class Entity<T> { 
    public Prime<T> prime(); 
} 

public class AbstractEntityA<T extends EntityA> extends Entity<T> { 
    @Override 
    public AbstractAPrime<T> prime(); 
} 

public class EntityA extends AbstractEntityA<EntityA>{ 
    public EntityAPrime prime() { ... } 
} 

public class EntityB extends AbstractEntityA<EntityB> { 
    public EntityBPrime prime() {...} 
} 


public class AbstractAPrime<T extends EntityA> extends AbstractPrime<T> {} 

public class EntityAPrime extends AbstractAPrime<EntityA>{} 
public class EntityBPrime extends AbstractAPrime<EntityB>{} 

Короче говоря, переместить содержимое в исходном EntityA для абстрактного класса, для которого будет продлен EntityA (в основном просто обычный распространяется) и EntityB (переопределяет и добавляет больше вещей). То же самое относится к Prime.

+0

У меня была ошибка в вопросе, который теперь исправлен. Спасибо, что заметили, и нашли время ответить. +1. Создание общих образов похоже на то, что это решило бы проблему, но в вопросе, который я упомянул, это было бы грязно. У нас есть более 100 объектов, и я думал, что все они должны стать родовыми. Кроме того, Hibernate не будет возвращать общие объекты, и я не уверен, что будет делать Spring Data. Набрав это, хотя я понял, что только сущности в иерархии наследования должны быть общими, и у нас их слишком много. –