2012-04-05 3 views
2

как в пути, чтобы проверить надлежащее использование статического реестра:Есть ли способ в Java, чтобы определить, вызван ли метод в статическом инициализаторе (или нет)?

class AClass { 
    static final IDType = IDregistry.registerId(...); 
} 

class IDRegistry { 
    public static registerId(...) 
    { 
      if(isCalledInStaticInitializer()) { 
       return(new IDType(...)); 
      } 
      assert false : "NO NO - can't do this !!!"; 
    } 
} 
+0

Не могли бы вы расширить свой прецедент немного больше? Я почти уверен, что вам действительно не нужно что-то делать. –

+0

Действительно, действительно плохая идея - но вы должны быть в состоянии сделать это с небольшим ходом и отражением стека. Но если вы не знаете, как это сделать, вероятно, это действительно хорошая идея, чтобы не попробовать этот материал. Я уверен, что если вы опишете, что вы на самом деле пытаетесь сделать, там будут лучшие способы. – Voo

+0

simple - если метод вызывается вне статического инициализатора, он не подходит с точки зрения того, как этот метод предназначен для использования. просто проверить этот случай ошибки и отметить неправильное использование. – peterk

ответ

3

Я не думаю, что вы должны это сделать. Но если вы настаиваете, то это получить вы начали:

public static boolean isCalledInStaticInitializer() 
{ 
    for (StackTraceElement ste : Thread.currentThread().getStackTrace()) 
    { 
     if("<clinit>".equals(ste.getMethodName())) 
     { 
      return true; 
     } 
    } 
    return false; 
} 

Источник: В разделе 2.9 JVM Specification («Специальные методы»):

«Класс или интерфейс имеет не более одного класса или интерфейса инициализации (инициализация) и инициализируется (п.5.5) путем вызова этого метода. Метод инициализации класса или интерфейса имеет специальное имя <clinit> «

+0

Thread.currentThread(). GetStackTrace(), вероятно, лучший способ получить стек вызовов. Кроме того, я думаю, что ste.getClassName() также должно быть проверено в предложении if перед возвратом true. – shams

+0

@shams Я изменил его, чтобы использовать 'Thread.currentThread(). GetStackTrace()', потому что он кажется более элегантным и более эффективным. Я не думаю, что он хочет проверить ste.getClassName(), потому что он заранее не знает, какие статические инициализаторы класса он защищает. –

+0

Я действительно «проверил» это - спасибо, что он отвечает * если * «» всегда * *, как определено Java VM именем метода, которое является и вызывается только для статической инициализации. Но просто кажется настолько ужасно неэлегантным :) Меня не волновало, что класс инициализируется просто, вызван ли он в контексте статического инициализатора или нет. – peterk

1

Ваше намерение не является необходимым (никто не делает - я никогда не видел его и т.д.).

То, что вы хотите, это просто так:

static final IDType id = new IDType(...); 

Если вам необходимо зарегистрировать идентификатор где-нибудь, поместить этот код в конструкторе IDType если IDType неизменен, или если это изменяемые (маловероятно) один вариант было бы использование фабричного метода в IDType для создания и регистрации:

public class IDType() { 
    ... 
    public static IDType createAndRegister(...) { 
     IDType idType = new IDType(...); 
     SomeClass.register(idType); 
     return idType; 
    } 
} 
+0

Выполнение 'this', доступное для других объектов до завершения конструктора, является распространенной причиной ошибок параллелизма. Я не думаю, что это хорошее предложение. – erickson

+0

@erickson Справедливая точка, но это зависит от того, изменена она или нет. Неизменяемые, после инициализации, могут быть безопасно опубликованы. – Bohemian

+0

Объект не полностью инициализирован до завершения его конструктора. (См. [JLS 17.5.] (Http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5)) Неизбежность недостаточна для обеспечения безопасности потоков.Второй код должен всегда использоваться; регистрация от конструктора, как предложено первым образцом и замечаниями, небезопасна. – erickson

0

Статическая инициализация выполняется для каждого класса. Поэтому вопрос, который вам может потребоваться задать самому себе, - это статическая инициализация этого класса? ». Из вашего примера использования похоже, что статическая инициализация IDRegistry запускается перед любым из классов, которые регистрируются. Вы можете использовать другой подход и посмотреть на что-то еще, что вы хотите применить к тому, что можно добавить в реестр.

BTW вы обнаруживаете в процессе инициализации одного класса, используя конечную переменную, которую вы объявляете в самом начале в классе, а затем устанавливаете в статическом блоке в самом конце класса. Поскольку статическая инициализация выполняется в исходном порядке, любой код, выполняемый как часть статической инициализации классов, будет видеть значение по умолчанию java для типа (т.е. false, 0, null), а любой код, выполняемый после статической инициализации, увидит окончательный набор стоимость.

class AClass { 
    static final boolean staticInitDone; 

    // Any static initialization done here will see 
    // staticInitDone as false, e.g. the 
    // Y constructor below would see false. 
    static X = new Y(); 


    static { 
     staticInitDone = true; 
    } 
} 

Но вы заметите, что это не подходит для координации кросс-класса, которую вы хотите.

+0

актуально просто проверить, будет ли она вызываться в * любой * статической инициализации. – peterk

Смежные вопросы