Подтвердить что ты не робот

Проблемы с инициализацией конечной переменной в Java

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

У меня есть свойство объекта, которое является окончательным, но динамическим. То есть, я хочу, чтобы значение было постоянным после назначения, но значение может быть различным для каждой среды выполнения. Поэтому я объявляю переменную уровня класса в начале класса - скажем private final FILE_NAME;. Затем в конструкторе присваиваю ему значение - скажем FILE_NAME = buildFileName();

Проблема начинается, когда у меня есть код в методе buildFileName(), который генерирует исключение. Поэтому я пытаюсь сделать что-то подобное в конструкторе:

try{
   FILE_NAME = buildFileName();
}
catch(Exception e){
   ...
   System.exit(1);
}

Теперь у меня есть ошибка - "Пустое конечное поле FILE_NAME, возможно, не было инициализировано". Именно здесь я начинаю слегка раздражаться в строгом компиляторе Java. Я знаю, что это не будет проблемой, потому что, если он доберется до улова, программа выйдет... Но компилятор этого не знает и поэтому не разрешает этот код. Если я попытаюсь добавить фиктивное задание в catch, я получу - "Окончательное поле FILE_NAME, возможно, уже было назначено". Я явно не могу назначить значение по умолчанию перед try-catch, потому что я могу назначить его только один раз.

Любые идеи...?

4b9b3361

Ответ 1

С другой стороны, я думаю, что я только что придумал решение! - используйте промежуточную переменную.

String fileName = null;
try{
   fileName = buildFileName();
}
catch(Exception e){
   ...
   System.exit(1);
}
FILE_NAME = fileName;

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

Ответ 2

Как насчет

String tempName = null;
try{
   tempName = buildFileName();
}
catch(Exception e){
   ...
   System.exit(1);
}
FILE_NAME = tempName;

Ответ 3

Либо

try {
   FILE_NAME = buildFileName();
} catch (Exception e){
   ...
   System.exit(1);
   throw new Error();
}

Или некоторые предпочитают:

private static final String FILE_NAME = fileName();

private static String fileName() {
    try {
        return buildFileName();
    } catch (Exception e){
        ...
        System.exit(1);
        throw new Error();
    }
}

Но вызов System.exit в статическом инициализаторе, вероятно, является плохой идеей. Это собирается испортить ваши тесты устройства.

Ответ 4

Я бы просто просто выбросил ошибку - если ваш поток ошибок правильно спроектирован, System.exit() должен быть избыточным. Ваша программа, по-видимому, не врезается в пустыню, если возникает ошибка...?

Ответ 5

В то же время, что и проблема OP, я должен был найти способ присвоить значения конечным полям, которые должны быть прочитаны из файла .properties в файловой системе, поэтому значения не могут быть известны моим до тех пор, пока это не произойдет. Использование вызова обобщенного метода для присвоения значения после прочтения содержимого файла .properties в объекте "Свойства" при запуске приложения - это графа "Град Мэри", который, к счастью, сработал. Это также ограничивает число. раз файл должен быть прочитан один раз для приложения, загружаемого в память, просто проверкой кода, чтобы увидеть, является ли объект "Свойства" или нет в настоящий момент null. Но, конечно же, после присвоения окончательное значение поля не может быть изменено, кроме как путем изменения его "окончательного" статуса путем создания определения модификации поля во время выполнения (как описано в некоторых других местах здесь на SO, таких как fooobar.com/info/18683/... - подлый, но мне это нравится!). Пример кода с типичной проверкой ошибок во время выполнения, например, для NPE, опущенных для краткости:

import java.util.Properties;

public class MyConstants {

  private static Properties props; // declared, not initialized,
                                   // so it can still be set to
                                   // an object reference.

  public static String MY_STRING = getProperty("prop1name", "defaultval1");
  public static int MY_INT = Integer.parseInt(getProperty("prop2name", "1"));
  // more fields...

  private static String getProperty(String name, String dflt) {
   if ( props == null ) {
     readProperties();
   }
   return props.getProperty(name, dflt);
  } 

  private static void readProperties() {
     props = new Properties(); // Use your fave way to read
                      // props from the file system; a permutation
                      // of Properties.load(...) worked for me.
  } 

  // Testing...
  public static void main(String[] args) {
      System.out.println(MY_STRING);
      System.out.println(MY_INT);
  }

}

Это позволяет вам выполнять экстернализацию свойств в приложении и по-прежнему отмечать поля, используемые для хранения их значений как "final". Он также позволяет вам гарантировать возвращаемое значение для конечного значения поля, так как getProperty() в классе "Свойства" позволяет передать код вызова метода по умолчанию для использования в случае, если пара значений ключа свойства не была найдена во внешнем .properties.