从泛型返回异常

时间:2018-08-21 14:28:24

标签: java spring generics exception-handling

我目前有多个类,即FacadeA ... FacadeZ,它们扩展了FacadeBaseFacadeBase应该具有(某种通用方法?),即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);
  }
}

3 个答案:

答案 0 :(得分:2)

您可以使用泛型,但这还不够,因为您需要在运行时实例化特定类型。
大致来说,您有两种方法:

  • 反射方式
  • 声明性方式

1)反射方式并不简单。 Java不能提供您所需的全部功能。您可以从Spring启发或使用像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参数,但总体思路是相同的。

答案 1 :(得分:1)

有两个很好的理由停在throws FacadeException

  • FacadeBase.checkFacade的合同总会迫使客户抓住FacadeException(假设进行接口编程的良好做法)
  • 异常类不能通用。尽管您可以在Exception子句中使用throws类型的参数,但这只是过度设计(除了违反最佳实践)

因此,子类应声明throws Facade<A...Z>Exception或完全忽略此子句(如果适用)。但这对客户端/调用者没有任何影响(除非他们采取不鼓励使用的声明子类类型的方法)

如果您需要检查在呼叫方出现的 异常,那么无论如何您都必须知道具体的异常类,因为您无法检查{{1 }}。

答案 2 :(得分:1)

据我所知,问题的核心是如何按照此伪代码示意表示的方式声明和实现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);
    }
  }
  ...
}

我首先注意到,将 characteristic 异常与具体的FacadeBase子类相关联会破坏抽象。子类当然可以抛出自己的特定异常,但是一旦其他类特别了解或关心这些异常,尤其是为了能够识别那些子类,抽象就会崩溃。

特别是,如果您的isFacadeAvailable(facadeId)方法与Java类路径中可用的FacadeBase子类有任何关系,那么这似乎与该Facade实现的典型异常可用相关。在那种情况下,当FacadeQException不可用时,您不能期望能够实例化FacadeQ,并且当在类路径中不存在任何异常时,很可能会遇到类加载失败。 / p>

第二,我注意到由于所有Facade[A-Z]Exception都扩展了FacadeExceptionFacadeBase.checkFacade()可以简单地声明FacadeException而不是声明所有单独的异常。如果确实仍然由该方法抛出异常,那将不会阻止其他代码捕获特定异常。

要实际抛出各个异常,您需要先构建它们,这需要大switch块,大if / then / {{ 1}}语句,工厂方法或适当的异常类的反射实例化,或它们的某种组合。请注意,异常是对象;可以将它们分配给变量并从方法中返回。 else语句可以抛出 any 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 // ... } 成员以传达不可用外观的 ID ,而不是抛出特定于外观的异常。或者,如果您不想将其放在FacadeException本身上,则定义一个承载它的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;
    }
}