2015-06-04 6 views
2

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

abstract class MyDomain{...} 
abstract class FooDomain extends MyDomain{...} 
abstract class BarDomain extends MyDomain{...} 
class FirstConcreteBarDomain extends BarDomain{...} 
class SecondConcreteBarDomain extends BarDomain{...} 

мне нужен завод, который создает MyDomain объекты. Моя первая попытка была такова:

public interface ISpecializedObjectsFactory { 
    public <T extends MyDomain> T create(Class<?> clazz); 
} 

Implementend как:

public class FirstSpecializedObjectsFactory implements ISpecializedObjectsFactory { 

    @Override 
    public <T extends MyDomain> T create(Class<?> clazz) { 
     if(clazz.equals(BarDomain.class)) 
      return new FirstBarDomain(); 
     throw new InvalidParameterException(); 
    } 

Same для SecondBarDomain.

ПЕРВЫЙ ВОПРОС: Почему это генерируется ошибка, которая говорит, что она не может бросить FirstBarDomain в T?

После этой ошибки я представил актерский состав: return (T) new FirstBarDomain();.

Проблема заключается в том, что приведение небезопасно, и я хочу быть уверенным в результате, поэтому я ввел еще одно ограничение (при условии, что каждый объект имеет MyDomain всегда 2 уровней вывода):

public <T extends AnagrafeDomain, S extends T> S create(Class<T> clazz) 

ВТОРОЙ ВОПРОС: Предполагая, что этот завод является единственной точкой входа, где создаются объекты MyDomain, и что вызовы на фабрику никогда не используют конкретные классы (но всегда такие: BarDomain subj = SpecializedObjectsFactory.getFactory().create(BarDomain.class);), вопрос в том, является ли эта новая версия безопасной ?

ответ

2

Причину литой небезопасна из-за эту конкретную линию:

public <T extends MyDomain> T create(Class<?> clazz) { 

Это выводит тип возвращаемого из сайт звонка; Другими словами, рассмотрим следующий класс:

public abstract class MyFakeDomain extends MyDomain { } 

Следующий код будет компилировать, но не во время выполнения:

ISpecializedObjectsFactory factory = new FirstSpecializedObjectsFactory(); 
MyFakeDomain broken = factory.create(BarDomain.class); 

Это выбросит ClassCastException из-за вывода типа; выведенный тип будет MyFakeDomain, в результате чего будет сделан листинг FirstBarDomain на MyFakeDomain, что является незаконным литом - отсюда и опасное предупреждение.

Тип вывода также является причиной, по которой должен присутствовать литой; в то время как FirstBarDomain определенно подкласс MyDomain, мы не знаем, если это типа T как T может быть любойMyDomain подкласс не обязательно FirstBarDomain.

Однако, если вызывающий абонент осторожен, ваш код будет работать нормально - считаете ли вы это приемлемым или нет, зависит от вас.

Это дает нам ответ на ваш второй вопрос: используя BarDomain, поскольку тип, который должен быть выведен, не всегда будет безопасным, поскольку это может быть другой подкласс MyDomain. Единственный тип, который всегда был бы безопасным здесь, - MyDomain - однако, если вы планируете использовать только тип MyDomain, вы можете также удалить общий тип привязки и просто сделать возвращаемый тип MyDomain.

+0

OP сказал:«звонки на заводе никогда не использовать конкретные классы» –

+0

@ sharonbn Правильно, но это все еще не делает его безопасным; даже если '' 'MyFakeDomain''' '' abstract''', это все равно приведет к той же проблеме. Я явно сделаю его абстрактным, чтобы избежать путаницы. – Toby

+0

Это должно и должно быть вызвано исключением класса. Разумеется, вы бы использовали интерфейс BarDomain для развязки кода из spesific-реализации. Точно так же, как вы предпочитаете 'List = ...' а не 'ArrayList = ...' – Ian2thedv

0

Ограничение, которое даст вам уверенность, которую вы ищете ограничивает классы, что ваш завод получает:

public interface ISpecializedObjectsFactory { 
    public <T extends MyDomain> T create(Class<? extends MyDomain> clazz); 
} 


public class FirstSpecializedObjectsFactory implements ISpecializedObjectsFactory { 
    @Override 
    public <T extends MyDomain> T create(Class<? extends MyDomain> clazz) { 
     if(clazz.equals(BarDomain.class)) 
      return (T) new FirstBarDomain(); 
     throw new InvalidParameterException(); 
    } 
} 

компилятор не будет принимать любой вызов, чтобы создать, когда аргумент не является подклассом MYDOMAIN , Однако он примет абстрактный класс. Если вы хотите знать, что вы получили конкретный класс, вы можете найти ответ здесь How can I determine whether a Java class is abstract by reflection

+0

Я думаю, что это решение будет принимать 'FooDomain Subj = SpecializedObjectsFactory.getFactory() создают (BarDomain.class);. ', И он будет бросать ClassCastException –

Смежные вопросы