从泛型返回异常
Returning exceptions from generics
我目前有多个 类,即 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);
}
}
您可以使用泛型,但这还不够,因为您需要在运行时实例化特定类型。
一般来说,你有两种方式:
- 反射方式
- 声明方式
1)反射方式不简单。 Java 没有提供您需要做的所有事情。您可以从 Spring 库中获得启发或使用库来做到这一点。
org.springframework.core.GenericTypeResolver
应该对你特别有帮助。
您可以使用 static Class<?> resolveTypeArgument(Class<?> clazz, Class<?> genericIfc)
方法:
Resolve the single type argument of the given generic interface
against the given target class which is assumed to implement the
generic interface and possibly declare a concrete type for its type
variable.
如:
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()
来实例化泛型的 class。
2) 声明方式更简单,但需要一些样板代码。
使您的摘要 class 成为通用的 class,指定异常类型并提供一个构造函数来存储要抛出的异常。
例如:
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();
}
}
}
Subclasses 应该与他们的供应商一起调用超级构造函数:
public class FacadeA extends FacadeBase<FacadeExceptionA>{
public FacadeA(Supplier<FacadeExceptionA> suppException) {
super(suppException);
}
}
作为替代方案,您可以将 Supplier
替换为 Class
参数,但总体思路是相同的。
停在 throws FacadeException
有两个很好的理由:
FacadeBase.checkFacade
的合同无论如何都会迫使客户赶上 FacadeException
(假设编程接口的良好实践)
- 异常classes 不能是通用的。尽管您可以在
throws
子句中使用 Exception
类型参数,但这只是过度设计(除了违反最佳实践之外)
所以 subclasses 应该声明 throws Facade<A...Z>Exception
或者在适用的地方完全省略这个子句。但这对 client/caller 没有影响(除非他们采取不鼓励的方式声明 subclass 类型)
如果你需要检查调用方引发了哪个异常,那么无论如何你都必须知道具体的异常class,因为无论如何你都无法检查 instanceof T
。
据我了解,问题的核心是如何按照此伪代码示意性表示的方式声明和实现 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
subclasses 相关联会破坏抽象。 subclasses 肯定会抛出它们自己的特定异常,但是一旦其他 classes 知道或关心那些特别的,特别是为了能够识别那些 subclasses , 抽象分崩离析。
特别是,如果您的 isFacadeAvailable(facadeId)
方法与 FacadeBase
的子 class 有任何关系,在 Java class 路径中可用,那么这似乎可能与外观实现的特征异常可用相关。在那种情况下,当 FacadeQ
不可用时,您不能期望能够实例化 FacadeQException
,并且当任何异常时,您可能会 运行 进入 class 加载失败class路径中不存在。
其次,我观察到因为所有 Facade[A-Z]Exception
扩展 FacadeException
,FacadeBase.checkFacade()
可以简单地声明 FacadeException
而不是声明所有单独的异常。这不会阻止其他代码捕获特定的异常,如果这些异常仍然被此方法抛出的话。
为了真正抛出个别异常,您需要先构建它们,这需要一个大的 switch
块,一个大的 if
/then
/ else
语句、工厂方法或适当异常的反射实例化 class,或它们的某种组合。请注意,异常是对象;它们可以分配给变量并从方法返回。一个 throw
语句可以抛出 any 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 // ...
}
HOWEVER,我强烈建议您改为考虑给 FacadeException
一个成员,通过它来传达不可用的 ID facade,而不是通过抛出特定于 facade 的异常来做到这一点。或者如果你不想把它放在 FacadeException
本身,然后定义一个 FacadeUnavailableException
subclass 携带它:
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);
}
}
我目前有多个 类,即 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);
}
}
您可以使用泛型,但这还不够,因为您需要在运行时实例化特定类型。
一般来说,你有两种方式:
- 反射方式
- 声明方式
1)反射方式不简单。 Java 没有提供您需要做的所有事情。您可以从 Spring 库中获得启发或使用库来做到这一点。
org.springframework.core.GenericTypeResolver
应该对你特别有帮助。
您可以使用 static Class<?> resolveTypeArgument(Class<?> clazz, Class<?> genericIfc)
方法:
Resolve the single type argument of the given generic interface against the given target class which is assumed to implement the generic interface and possibly declare a concrete type for its type variable.
如:
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()
来实例化泛型的 class。
2) 声明方式更简单,但需要一些样板代码。
使您的摘要 class 成为通用的 class,指定异常类型并提供一个构造函数来存储要抛出的异常。
例如:
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();
}
}
}
Subclasses 应该与他们的供应商一起调用超级构造函数:
public class FacadeA extends FacadeBase<FacadeExceptionA>{
public FacadeA(Supplier<FacadeExceptionA> suppException) {
super(suppException);
}
}
作为替代方案,您可以将 Supplier
替换为 Class
参数,但总体思路是相同的。
停在 throws FacadeException
有两个很好的理由:
FacadeBase.checkFacade
的合同无论如何都会迫使客户赶上FacadeException
(假设编程接口的良好实践)- 异常classes 不能是通用的。尽管您可以在
throws
子句中使用Exception
类型参数,但这只是过度设计(除了违反最佳实践之外)
所以 subclasses 应该声明 throws Facade<A...Z>Exception
或者在适用的地方完全省略这个子句。但这对 client/caller 没有影响(除非他们采取不鼓励的方式声明 subclass 类型)
如果你需要检查调用方引发了哪个异常,那么无论如何你都必须知道具体的异常class,因为无论如何你都无法检查 instanceof T
。
据我了解,问题的核心是如何按照此伪代码示意性表示的方式声明和实现 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
subclasses 相关联会破坏抽象。 subclasses 肯定会抛出它们自己的特定异常,但是一旦其他 classes 知道或关心那些特别的,特别是为了能够识别那些 subclasses , 抽象分崩离析。
特别是,如果您的 isFacadeAvailable(facadeId)
方法与 FacadeBase
的子 class 有任何关系,在 Java class 路径中可用,那么这似乎可能与外观实现的特征异常可用相关。在那种情况下,当 FacadeQ
不可用时,您不能期望能够实例化 FacadeQException
,并且当任何异常时,您可能会 运行 进入 class 加载失败class路径中不存在。
其次,我观察到因为所有 Facade[A-Z]Exception
扩展 FacadeException
,FacadeBase.checkFacade()
可以简单地声明 FacadeException
而不是声明所有单独的异常。这不会阻止其他代码捕获特定的异常,如果这些异常仍然被此方法抛出的话。
为了真正抛出个别异常,您需要先构建它们,这需要一个大的 switch
块,一个大的 if
/then
/ else
语句、工厂方法或适当异常的反射实例化 class,或它们的某种组合。请注意,异常是对象;它们可以分配给变量并从方法返回。一个 throw
语句可以抛出 any 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 // ...
}
HOWEVER,我强烈建议您改为考虑给 FacadeException
一个成员,通过它来传达不可用的 ID facade,而不是通过抛出特定于 facade 的异常来做到这一点。或者如果你不想把它放在 FacadeException
本身,然后定义一个 FacadeUnavailableException
subclass 携带它:
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);
}
}