Проблемы инициализации финальной переменной в Java - PullRequest
20 голосов
/ 05 мая 2010

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

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

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

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

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

Есть идеи ...?

Ответы [ 5 ]

20 голосов
/ 05 мая 2010

Как насчет

String tempName = null;
try{
   tempName = buildFileName();
}
catch(Exception e){
   ...
   System.exit(1);
}
FILE_NAME = tempName;
7 голосов
/ 05 мая 2010

Либо

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 голосов
/ 05 мая 2010

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

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

Не знаю, почему я так долго думал об этом ...

1 голос
/ 05 мая 2010

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

0 голосов
/ 29 октября 2014

В том же ключе, что и проблема ОП, мне нужно было найти способ присвоить значения конечным полям для чтения из файла .properties в файловой системе, поэтому эти значения не могут быть известны моимприложение, пока это не произошло.Использование обобщенного вызова метода для присвоения значения после считывания содержимого файла .properties в объект Properties при запуске приложения было удачным проходом Hail Mary.Это также ограничивает нет.раз файл должен быть прочитан один раз для загрузки приложения в память просто путем проверки кода, чтобы видеть, является ли объект Properties в настоящее время нулевым или нет.Но, разумеется, после назначения значение окончательного поля не может быть изменено, кроме как путем изменения его «конечного» состояния посредством манипуляции изменяющим определением поля во время выполнения (как обсуждалось в некоторых других местах здесь, в SO, например, https://stackoverflow.com/a/3301720/1216686 -подлый, но я люблю это!).Пример кода, с типичной проверкой ошибок во время выполнения, такой как для 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 позволяет вызывающему коду метода передавать значение по умолчанию для использования в случае, если пара ключ-значение свойства не была найдена во внешнемФайл .properties.

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