在对象构建过程中从try-with-resources分别从body中捕获错误

时间:2014-11-15 07:06:34

标签: java try-with-resources autocloseable

摘要

我有一个可关闭的类型CloseableClass,它可以在其构造函数,方法中引发IOError,甚至可能在close内。我想使用try-with-resources并且仍然在构造期间处理错误与使用期间的错误不同(使用包括清理)。更好的是,我想编写可维护的代码。


假设您希望构造一个可关闭的类实例,并将其与try-with-resources语句一起使用。它可以在其构造函数和try-with-resources主体中使用的方法中抛出IOException

import java.io.Closeable;
import java.io.IOException;
import java.util.Random;

public class CloseableClass implements Closeable {
    public CloseableClass() throws IOException {
        if (new Random().nextBoolean()) {
            throw new IOException();
        }
    }

    public void internetStuff() throws IOException {
        if (new Random().nextBoolean()) {
            throw new IOException();
        }
    }

    public void close() throws IOException {
        if (new Random().nextBoolean()) {
            throw new IOException();
        }
    }

    public static void main(String[] args) {
        try (CloseableClass closeable = new CloseableClass()) {
            closeable.internetStuff();
        }
        catch (IOException e) {
            System.out.println("Bad error!");
        }
    }
}

假设你想要分别处理构造函数和主体中抛出的错误。是否有支持的方式来做到这一点?在Python中我会这样做:

try:
    closeable = CloseableClass()
except IOException:
    print("Constructor error")
    return

try:
    with closeable:
        closeable.internet_stuff()
except IOException:
    print("Body error")

但是在Java中你不能不为对象指定第二个名字:

CloseableClass closeable_;

try {
    closeable_ = new CloseableClass();
}
catch (IOException e) {            
    System.out.println("Constructor error!");
    return;
}

try (CloseableClass closeable = closeable_) {
    closeable.internetStuff();
}
catch (IOException e) {
    System.out.println("Body error!");
}

我被告知这是“不可维护的代码”,主要是因为closeable_的使用,我不太同意。我希望避免使用try-finally,因为那时你会遇到更糟糕的模仿问题:

CloseableClass closeable;

try {
    closeable = new CloseableClass();
}
catch (IOException e) {            
    System.out.println("Constructor error!");
    return;
}

try {
    closeable.internetStuff();
}
catch (IOException e) {
    try {
        closeable.close();
    }
    catch (IOException ignore) {
        // Already dealing with this
    }

    System.out.println("Body error!");
}
finally {
    try {
        closeable.close();
    }
    catch (IOException e) {
        System.out.println("Body error!");
    }
}

请注意,这需要第二次调用close为无操作,测试类不遵守该操作(注意AutoCloseable不需要此操作,尽管{{1}确实)。 <{1}}不能抛出,但不是很多。

基本上问题是

  • Closeable可以抛出
  • 在处理close之前关闭,以防止打印close两次
  • 如何使其与try-with-resources
  • 中的多个初始化程序一起使用并不明显
  • 无论如何,你最终会复制代码。

我只是被迫忍受“不可维护的代码”,还是我忽略了一个处理这个问题的好方法?

2 个答案:

答案 0 :(得分:1)

&#39;请注意,这需要第二次调用才能成为无操作 - 不,您不需要close() catch阻止,因为finally块将始终执行。如果您使用close()块中的catch之类的调用终止JVM,则只会在System.exit()块内使用catch。通常,您会从Exception时钟向调用者抛出catch,但您将在finally阻止大部分时间执行清理。

尝试使用资源更好,但您可以使用Exception的类型和描述来解释错误的地方和位置。

修改

据我所知,我建议:

1)尝试使用资源:

try(Resource resource = new Resource()){
    // use resource.
}catch(Exception e){
    // handle exception.
    // OR better to throw exception to caller.
    throw e;
}

2)传统风格:

Resource resource = null;
try{
    resource = new Resource();
    // use resource
}catch(Exception e){
    // handle exception.
    // OR better to throw exception to caller.
    throw e;
} finally {
   if(resource != null){
       try{
           resource.close();
       } catch(Exception e){
           // most of time you wont or cant do anything here.
       }
   }
}

答案 1 :(得分:1)

一种解决方案是定义一种将初始化错误包装在自定义异常类型中的方法,然后使用该方法确定何时发生错误。

private CloseableClass createCloseable() throws CloseableCreateException{
    try {
        return new CloseableClass();
    } except (IOException e) {
        throw new CloseableCreateException(e);
    }
}
try (CloseableClass closeable = initCloseable()) {
    closeable.internetStuff();
} catch (CloseableCreateException e) {
    System.out.println("Constructor error!");
} catch (IOException e) {
    System.out.println("Body error!");
}

另一种简单但不太精致的解决方案是使用布尔标志:

boolean init = true;
try (CloseableClass closeable = new CloseableClass()) {
    init = false;
    closeable.internetStuff();
} catch (IOException e) {
    if (init) {
        System.out.println("Constructor error!");
    } else {
        System.out.println("Body error!");
    }
}