【问题标题】:Is there any way to don't use 'instance of' with objects that I can't extend?有没有办法不对我无法扩展的对象使用“实例”?
【发布时间】:2015-07-15 12:58:02
【问题描述】:

我有一个自定义异常,它具有几个继承自 Exception 类的属性。

根据异常的实例,我想返回一个代码。 代码:

public int manageException(Exception exception) {               
    int code = 0;               

    if (exception instanceof MyCustomException) {       
        code = ((MyCustomException) exception).getCode();       
    } else if (exception instanceof NestedRuntimeException) {       
        code = 444;         
    } else if (exception instanceof HibernateException) {       
        code = 555;         
    } else {        
        code = 666;
    }       

    return code;
}

【问题讨论】:

  • 如果你把它放在try-catch 块的多个catch 子句中,你就不必使用instanceof
  • 我实现这个方法是因为我应该发送带有错误的邮件。所以,我只想在一个地方管理异常。
  • 我假设您使用的是 Java 8?
  • 有一种复杂的 lambdas 方式(但在 Java 7 中没有),但我不知道我会说它比 instanceof 更好。如果您确实需要在多个地方处理这些异常,这可能是少数情况下它是适合该工作的工具的情况之一。您确定您没有过早/过深地处理异常吗?
  • 是的,我也这么认为。我只是好奇。

标签: java oop exception exception-handling


【解决方案1】:

如果

  • 您需要在多个位置处理这些异常,并且

  • 您不希望每个位置有多个 catch 块(每个异常类型一个)

...那么instanceof 与您在 Java 7 中可能获得的一样干净。

话虽如此,您可以这样做:

public void manageException(Runnable r) {
    try {
        r.run();
    }
    catch (NestedRuntimeException nre) {
        throw new MyCustomException(444, nre);
    }
    catch (HibernateException he) {
        throw new MyCustomException(555, he);
    }
    catch (Exception e) {
        throw new MyCustomException(666, e);
    }
}

...然后在任何你需要的地方:

try {
    this.manageException(new Runnable() {
        @Override
        public void run() {
            // Do something
        }
    });
}
catch (MyCustomException mce) {
    int code = mce.getCode();
}

...但它并没有给你买多少,而且真的很丑。 :-)

在 Java 8 中,它干净多了。 manageException 是一样的,但是调用只是:

try {
    this.manageException(() => {
        // Do something here
    });
}
catch (MyCustomException mce) {
    int code = mce.getCode();
}

对我来说,Java 8 版本几乎开始胜过instanceof。 Java 7 版本,没那么多。

(为什么是上面的Runnable?因为JDK作者决定不定义一个不接受参数且没有返回值的新标准functional interfacemore in this question。他们推广了Runnable的概念。如果语义困扰您(他们会打扰我),您可以定义自己的。)

【讨论】:

    【解决方案2】:

    你可以像这样使用重载:

    public int manageException(MyCustomException e) {
        return e.getCode();
    }
    
    public int manageException(NestedRuntimeException e) {
        return 444;
    }
    
    public int manageException(HibernateExceptionexception e) {
        return 555;
    }
    
    public int manageException(Exception e) {
        return 666;
    }
    

    在@T.J. 发表评论后进行编辑。克劳德:

    请记住,您仍然需要多个 catch 块才能调用正确的方法。重载基于异常的 compile-time 类型。只需执行catch (Exception e) { int code = this.manageException(ex); } 将始终返回666

    【讨论】:

    • @user1750751:这仍然意味着多个catch 块,因为在编译时根据您传入的声明类型选择重载,所以我'我不确定它会给你买什么。只需 catch (Exception ex) { int code = this.manageException(ex); } 将始终返回 666
    • 哦,好的。我会检查的。
    • 支持者:我不认为你理解这个问题。这个答案没有任何帮助。比较:pastie.org/10294519pastie.org/10294517
    • 好点@T.J.Crowder。我同意。我想我仍然回答了这个问题,因为我没有使用 instanceof。 :)
    • @T.J.Crowder 是对的,我认为这种方法实际上没有任何用处,因为您可能不知道异常的实际类型。因此,要调用这些方法,OP 将不得不使用 multi catch 或 instanceof 和 cast,这是他首先要避免的
    【解决方案3】:

    如果您想将异常类型映射到不同的错误代码,您可以使用映射:

    Map<Class<? extends Exception>, Integer> map = new HashMap<> ();
    map.put (Exception.class, 5);
    map.put (NullPointerException.class, 42);
    
    try {
      throw null; //throws NPE
    } catch (Exception e) {
      System.out.println (map.get (e.getClass ())); //output is 42
    }
    

    我认为这很容易消耗,因为您可以从配置文件中读取映射而不是对其进行硬编码,因此您可以添加其他异常和错误代码而无需更改代码。

    您必须测试 map.get() 的返回值是否为 null,因为它可能是您之前未指定的异常,因此没有映射到它的 Integer。

    注意:正如第一条评论中提到的,这仅在您希望将类精确映射到错误代码时才有效。如果异常的子类与其父类具有相同的错误代码,则此解决方案将无法正常工作。

    【讨论】:

    • 问题是,如果在公共 API 中声明的异常类型是 FooException 但实际抛出的是内部 FooSubclassException,那么即使据您所知,您的映射也会失败,这是FooException。一般来说,您不能依赖于等同于类实例。
    • 没错,我没想到。一个可能的解决方案是编写一个查看地图的容器,如果没有找到映射。为每个 Maps 条目调用 clazz.isAssignableFrom(exceptionClazz)。然后,如果有多个可能的类,您将不得不决定使用哪一个(可能是最专业的一个)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多