Необработанные исключения при инициализации полей - PullRequest
26 голосов
/ 22 июня 2009

Есть ли в Java какой-либо синтаксис для управления исключениями, которые могут генерироваться при объявлении и инициализации переменной-члена класса?

public class MyClass
{
  // Doesn't compile because constructor can throw IOException
  private static MyFileWriter x = new MyFileWriter("foo.txt"); 
  ...
}

Или такие инициализации всегда нужно перемещать в метод, в котором мы можем объявить throws IOException или обернуть инициализацию в блок try-catch?

Ответы [ 7 ]

20 голосов
/ 22 июня 2009

Использовать статический блок инициализации

public class MyClass
{
  private static MyFileWriter x;

  static {
    try {
      x = new MyFileWriter("foo.txt"); 
    } catch (Exception e) {
      logging_and _stuff_you_might_want_to_terminate_the_app_here_blah();
    } // end try-catch
  } // end static init block
  ...
}
3 голосов
/ 23 июня 2009

Исключение, создаваемое статическим инициализатором, может указывать на проблему проектирования. На самом деле вы не должны пытаться загрузить файлы в статику. Кроме того, static не должен быть изменчивым.

Например, работая с JUnit 3.8.1, вы могли почти использовать его из апплета / WebStart, но это не удалось из-за одного статического инициализатора, осуществляющего доступ к файлу. Остальная часть задействованного класса отлично вписывалась в контекст, просто этот кусочек статики не соответствовал контексту и разрушал весь фреймворк.

В некоторых законных случаях возникает исключение. Если это тот случай, когда среда не имеет конкретной функции, скажем, потому что это старый JDK, то вы можете заменить реализации, и в этом нет ничего необычного. Если класс на самом деле не работает, создайте непроверенное исключение, а не разрешайте существование сломанного класса.

В зависимости от ваших предпочтений и имеющейся проблемы, есть два распространенных способа обойти это: явный статический инициализатор и статический метод. (Я, и я думаю, что большинство людей предпочитают первое; я считаю, что Джош Блох предпочитает второе.)

private static final Thing thing;

static {
    try {
        thing = new Thing();
    } catch (CheckedThingException exc) {
        throw new Error(exc);
    }
}

Или

private static final Thing thing = newThing();

private static Thing newThing() {
    try {
        return new Thing();
    } catch (CheckedThingException exc) {
        throw new Error(exc);
    }
}

Примечание: статика должна быть окончательной (и, как правило, неизменяемой). Будучи окончательным, правильное одиночное назначение проверяется вашим дружественным компилятором. Определенное присваивание означает, что оно может отловить прерванную обработку исключений - переносить и выбрасывать, не печатать / регистрировать. Как ни странно, вы не можете использовать имя класса, чтобы квалифицировать инициализацию с именем класса в статическом инициализаторе (я уверен, что для этого есть веская причина).

Инициализаторы экземпляров похожи, хотя вы можете заставить конструктор бросить или вы можете поместить инициализатор в конструктор.

3 голосов
/ 22 июня 2009

Лучше всего перенести эти виды инициализации в методы, которые могут обрабатывать исключения.

2 голосов
/ 22 июня 2009

Это строительство незаконно, как вы обнаружили. Члены, конструкторы которых генерируют проверенные исключения, не могут быть созданы, за исключением случаев, когда они находятся в контексте, который допускает обработку исключений, например конструктор, инициализатор экземпляра или (для статического члена) статический инициализатор.

Так что это был бы законный способ сделать это:

public class MyClass {
    MyFileWriter x;
    {
        try {
            x = new MyFileWriter("foo.txt");
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    ...
}

Законно, но довольно некрасиво. Я бы предпочел либо инициализировать его в конструкторе и объявить там исключение, либо заставить пользователя вызывать метод для явной инициализации. Но если пользователь должен инициализировать его, вы должны учитывать любые зависимые методы для возможности недопустимости объекта.

Если вы пишете MyClass в качестве оболочки для MyFileWriter, я бы сказал, что нужно выполнить инициализацию в конструкторе. В противном случае, я бы сначала спросил, нужно ли открывать писателя в течение всего времени существования объекта. Это может быть возможно рефакторинг это прочь.

Редактировать: Когда я писал это, "static" не было добавлено в поле. Это немного меняет дело: теперь я хотел бы знать, почему, черт возьми, вы хотите, чтобы писатель был открыт на всю жизнь загрузчик классов . Как это могло быть когда-либо закрыто?

Это какая-то домашняя система регистрации? Если это так, я бы посоветовал вам взглянуть на java.util.logging или на любую из множества хороших сторонних систем ведения журналов.

0 голосов
/ 14 марта 2015

Существует другой способ обработки исключений при инициализации полей. Давайте рассмотрим ваш случай, когда конструктор MyFileWriter выдает исключение. Рассмотрим этот пример кода для справок.

import java.io.IOException;
public class MyFileWriter {
    public MyFileWriter(String file) throws IOException{
         throw new IOException("welcome to [java-latte.blogpspot.in][1]");
    }
}

Если мы попытаемся инициализировать файл следующим образом ...

public class ExceptionFields{
    MyFileWriter f = new MyFileWriter("Hello");

}

Компилятор не позволит нам инициализировать это. Вместо инициализации в статическом блоке, вы можете сделать это

Объявить конструктор по умолчанию, который выбрасывает IOException

import java.io.IOException;
public class ExceptionFields{
    MyFileWriter f = new MyFileWriter("Hello");
    public ExceptionFields() throws IOException{    

    }    
}

Это инициализирует ваш объект MyFileWriter.

0 голосов
/ 22 июня 2009

Я предлагаю заводской метод:

  public class MyClass{
      private static MyFileWriter fileWriter = MyFileWriter.getFileWriter("foo.txt");

  }

  public class MyFileWriter {
     /*
      * Factory method. Opens files, etc etc 
      * @throws IOException.
      */
     public static MyFileWriter getFileWriter(String path) throws IOException{

        MyFileWriter writer  = new FileWriter();

       //stuff that can throw IOException here.

       return writer;
     }

   /*protected constructor*/
    protected MyFileWriter(){

     //build object here.

    } 
  }
0 голосов
/ 22 июня 2009

Если у класса есть только один конструктор, я обычно перемещаю такие инициализаторы в этот конструктор.

Если класс имеет более одного конструктора, я использую блок инициализации:

public class MyClass {
   private static MyFileWriter x;

   // initialization block start here
   {
       try {
          x = new MyFileWriter("..");
       } catch(Exception e) {
         // exception handling goes here
       }
   }

   public MyClass() { 
    // ctor #1
   }

   public MyClass(int n) { 
     // ctor #2
   }
}

Хорошая вещь об инициации. Блок заключается в том, что он «внедряется» в начало каждого конструктора. Таким образом, вам не нужно дублировать инициализаторы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...