28 Ağustos 2020 Cuma

Try With Resources

Giriş
try-with-resources kullanımıyla finally yazmaya gerek kalmıyor. Aslında finally bazen kafa karıştırıcı olabilir.

Bu konuyu açıklayan bazı yazılar yanlış anlamaya sebep oluyor. Sanki close içinde exception fırlatılırsa finally block içinden dışarıya sızabilir gibi anlaşılıyor.

Öyle olmadığını bu yazıda göstermeye çalıştım

1. Kullanım
Örnek
Kapatılmasını istediğimiz her kaynak bir değişkene atanmak zorundadır. Şöyle yaparız
try (Connection connection = DriverManager.getConnection(...);
     Statement statement = connection.createStatement();
     ResultSet resultSet = statement.executeQuery(sql)) {
    // ...   
}
Bazen kapatılan kaynak kendisine constructor içinde verilen kaynağı da kapatır. Bu durumda şöyle yapabiliriz
try (BufferedWriter bw = new BufferedWriter(new FileWriter(...)) {
    // ...   
}
Üretilen Kod
Örnek
Elimizde şöyle bir kod olsun
class MyBufferedReader extends BufferedReader {
  public MyBufferedReader(Reader in, int sz) {
    super(in, sz);
  }

  @Override
  public void close() throws IOException {
    super.close();
    throw new IOException("test");
  }
}

private void readFromMyBufferedReader() {
  Reader reader = new StringReader("test");
  try (MyBufferedReader myBufferedReader = new MyBufferedReader(reader, 1024)) {
    int read = myBufferedReader.read();
  } catch (IOException exception) {
    // Prints test
    log.error("Exception", exception);
  }
}
Üretilen kod şöyle
private void readFromMyBufferedReader() {
  Reader reader = new StringReader("test");

  try {
    MyBufferedReader bufferedReader = new MyBufferedReader(reader, 1024);

    try {
      int readValue = bufferedReader.read();
    } catch (Throwable readException) { // Üretilen kod
      try {
        bufferedReader.close();
      } catch (Throwable closeException) {
        readException.addSuppressed(closeException);
      }

      throw readException;
    }

      bufferedReader.close(); // Üretilen close() çağrısı
  } catch (IOException ioException) {
    log.error("Exception", ioException);
  }
}

2. constructor İçinde Exception Fırlatılırsa
Sadece finally işletilir
Örnek
Elimizde şöyle bir kod olsun
public static class Resource implements AutoCloseable {

  public Resource() throws Exception {
    throw new Exception("Exception from constructor");
  }

  public void doSomething() throws Exception {
    throw new Exception("Exception from method");
  }

  @Override
  public void close() throws Exception {
    throw new Exception("Exception from closeable");
  }
}

public static void main(String[] args) {
  try(Resource r = new Resource()) {
    r.doSomething();
  } catch (Exception ex) {
    ex.printStackTrace();
  }
}
Yakalanan exception nesnesinin detailMessage alanında "Exception from constructor" görürüz. 

3. try İçinde Exception Fırlatılırsa
Throwable nesnesinin suppressed alanında görebiliriz.

Örnek
Şöyle yaparız
try (Connection c = DriverManager.getConnection(url)) {
    work();
} catch (Exception e) {
    throw new Error("Insert failed", e);
}
Eğer hem work() hem de Connection.close() metodu içinde exception fırlatılırsa close() tarafından fırlatılan exception Throwable nesnesinin suppressed alanında görürüz.
Açıklaması şöyle
This will close your connection or generally the passed AutoCloseable appropriately. And will handle the shadowing of the exception by using the Throwable.addSuppressed() method.
Çıktı olarak şuna benzer bir şey alırız. Koyu kısım suppressed olan ve close() metodundan gelen exception.
>>> work()
  >>> close()
  MyException: Exception in work()
    at AutoClose.work(AutoClose.java:11)
    at AutoClose.main(AutoClose.java:16)    Suppressed: java.lang.RuntimeException: Exception in close()
      at AutoClose.close(AutoClose.java:6)
      at AutoClose.main(AutoClose.java:17)

Hiç yorum yok:

Yorum Gönder