В настоящее время у меня есть несколько классов, то есть FacadeA ... FacadeZ, которые расширяют FacadeBase. FacadeBase должен иметь (какой-то общий метод?), Т.е. checkFacade, который выдает исключительную ситуацию FacadeAException ... FacadeZException, которая расширяет FacadeException.

Это в настоящее время, насколько я получил. Я застрял с общей частью (или что-то, что решило бы мою проблему).

И да, я знаю, что не могу создать экземпляр дженериков, как написано в моем примере.

public abstract class FacadeBase {
  public void checkFacade(final String facadeId) throws Facade<A...Z>Exception {
    if (!isFacadeAvailable(facadeId)) {
      throw new Facade<A...Z>Exception(facadeId);
    }
  }
  ...
}

public class FacadeA extends FacadeBase {
  public void doSomethingWithFacadeA(final String facadeId) throws FacadeAException {
    checkFacade(facadeId));
    ...
  }
}

public class FacadeAException extends FacadeException {
  private FacadeAException(final String message) {
    super(message);
  }
}

public abstract class FacadeException extends Exception {
  private FacadeException(final String message) {
    super(message);
  }
}
0
user871611 21 Авг 2018 в 17:28

3 ответа

Лучший ответ

Вы можете использовать дженерики, но этого будет недостаточно, потому что вам нужно создать экземпляр определенного типа во время выполнения.
В целом у вас есть два пути:

  • путь отражения
  • декларативный способ

1) Путь отражения не прост. Java не предоставляет всего, что вам нужно для этого. Вы можете вдохновить или использовать библиотеку как Spring, чтобы сделать это.
org.springframework.core.GenericTypeResolver должен особенно помочь вам.
Вы можете использовать
static Class<?> resolveTypeArgument(Class<?> clazz, Class<?> genericIfc) метод, который:

Разрешить аргумент единственного типа данного универсального интерфейса с заданным целевым классом, который, как предполагается, реализует универсальный интерфейс и, возможно, объявит конкретный тип для его переменной типа.

Такие как :

public abstract class FacadeBase<T extends FacadeException> {

    private final Class<T> genericClazz;
    public FacadeBase () {
        this.genericClazz = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), FacadeBase.class);    
    }
}

Теперь у вас есть способ создать экземпляр класса универсального с помощью genericClazz.newInstance() или лучше Constructor.newInstance().

2) Декларативный способ проще, но требует некоторого кода котельной плиты.

Сделайте ваш абстрактный класс универсальным классом, который задает тип исключения и предоставляет конструктор, который хранит исключение для выброса.
Например :

public abstract class FacadeBase<T extends FacadeException> {

    private Supplier<T> suppException;

    public FacadeBase(Supplier<T> suppException) {
        this.suppException = suppException;
    }

    public void checkFacadeAvailability(final String facadeId) throws T {
        if (!isFacadeAvailable(facadeId)) {
            throw suppException.get();
        }
    }
}

Подклассы должны вызывать супер-конструктор со своим поставщиком:

public class FacadeA extends FacadeBase<FacadeExceptionA>{

    public FacadeA(Supplier<FacadeExceptionA> suppException) {
        super(suppException);
    }   
}

В качестве альтернативы вы можете заменить Supplier параметром Class, но общая идея та же.

2
davidxxx 21 Авг 2018 в 15:13

Насколько я понимаю, суть проблемы заключается в том, как объявить и реализовать метод checkFacade() в соответствии со схемой, представленной этим псевдокодом:

public abstract class FacadeBase {
  public void checkFacade(final String facadeId) throws Facade<A...Z>Exception {
    if (!isFacadeAvailable(facadeId)) {
      throw new Facade<A...Z>Exception(facadeId);
    }
  }
  ...
}

Прежде всего я отмечаю, что ассоциирование исключений признака с конкретными подклассами FacadeBase нарушает абстракцию. Подклассы, безусловно, могут выдавать свои собственные конкретные исключения, но как только другие классы узнают или заботятся о них конкретно, особенно, чтобы иметь возможность распознавать эти подклассы, абстракция распадается.

В частности, если ваш метод isFacadeAvailable(facadeId) имеет какое-либо отношение к подклассам FacadeBase, доступным в пути к классам Java, то, вероятно, это будет связано с тем, что будет доступно характерное исключение этой реализации фасада. В этом случае вы не можете ожидать, что сможете создать экземпляр FacadeQException, когда FacadeQ недоступен, и вы, скорее всего, столкнетесь с ошибками загрузки классов, когда какое-либо исключение отсутствует в пути к классам.

Во-вторых, я наблюдаю, что, поскольку все Facade[A-Z]Exception расширяются FacadeException, FacadeBase.checkFacade() могут просто объявить FacadeException вместо объявления всех отдельных исключений. Это не помешает другому коду перехватить определенные исключения, если они все еще создаются этим методом.

Для того, чтобы на самом деле выдать отдельные исключения, вам нужно сначала создать их, и это требует либо большого switch блока, либо большого if / then / else оператор, фабричный метод, или рефлексивное создание экземпляра соответствующего класса исключения, или некоторая их комбинация. Обратите внимание, что исключения являются объектами; они могут быть назначены переменным и возвращены из методов. Оператор throw может выбросить любой Throwable; это не должно быть вновь созданным. Таким образом, вы могли бы рассмотреть что-то вроде этого:

public void checkFacade(final String facadeId) throws FacadeException {
    if (!isFacadeAvailable(facadeId)) {
        throw createFacadeException(facadeId);
    }
}

private FacadeException createFacadeException(String facadeId) {
    if ("A".equals(facadeId)) {
        return new FacadeAException();
    } else // ...
}

ОДНАКО , я настоятельно рекомендую вам вместо этого дать FacadeException элемент, с помощью которого можно передать ID недоступного фасада, вместо того, чтобы делать это путем конкретное исключение. Или, если вы не хотите помещать это в сам FacadeException, то определите подкласс FacadeUnavailableException, который его переносит:

public class FacadeUnavailableException extends FacadeException {
    private final String facadeId;

    private FacadeAException(String message, String facadeId) {
        super(message);
        this.facadeId = facadeId;
    }

    public String getFacadeId() {
        return facadeId;
    }
}

С этим ваша проблема становится намного проще:

public void checkFacade(final String facadeId) throws FacadeUnavailableException {
    if (!isFacadeAvailable(facadeId)) {
        throw new FacadeUnavailableException("unavailable", facadeId);
    }
}
1
John Bollinger 21 Авг 2018 в 15:24

Есть две веские причины остановиться на throws FacadeException:

  • Контракт FacadeBase.checkFacade в любом случае заставит клиентов ловить FacadeException (при условии хорошей практики программирования для взаимодействия)
  • Классы исключений не могут быть общими. Несмотря на то, что вы можете использовать параметр типа Exception в предложении throws, это просто чрезмерное проектирование (в дополнение к идеям)

Поэтому подклассы должны объявлять throws Facade<A...Z>Exception или полностью опускать это предложение, где это применимо. Но это не имеет значения для клиента / вызывающего абонента (если они не выберут обескураженный способ объявления типов подкласса)

Если вам нужно проверить , какое исключение было вызвано на стороне вызывающей стороны, вам все равно придется знать конкретный класс исключений, потому что вы все равно не можете проверить instanceof T ,

1
ernest_k 21 Авг 2018 в 14:53
51950949