Когда синтетический флаг модификатора доступа к байт-коду JVM 0x1000 (шестнадцатеричный) установлен?

Для некоторого проекта синтаксического анализатора байт-кода Java я прочитал спецификацию JVM и выяснил, что значения битовой маски полей модификатора доступа к файлу класса виртуальной машины Java:

  ACC_PUBLIC = 0x0001
  ACC_FINAL = 0x0010
  ACC_SUPER = 0x0020 # old invokespecial instruction semantics (Java 1.0x?)
  ACC_INTERFACE = 0x0200
  ACC_ABSTRACT = 0x0400
  ACC_SYNTHETIC = 0x1000 
  ACC_ANNOTATION = 0x2000
  ACC_ENUM = 0x4000

Почему-то я понятия не имею, для чего нужен 0x1000. Я видел это один раз во внутреннем классе, но для всех внутренних классов, которые я проверял с тех пор, этот флаг никогда не устанавливался. Знаете ли вы, что означает этот флаг и где/когда он установлен?


person Peter Kofler    schedule 16.12.2011    source источник


Ответы (2)


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

Вы уже упомянули внутренние классы в качестве примера, где синтетические элементы вставляются компилятором Java, поэтому давайте рассмотрим такой класс:

class Foo {

  private String foo;

  class Bar {

    private Bar() { }

    String bar() {
      return foo;
    }
  }

  Bar bar() {
    return new Bar();
  }
}

Это прекрасно компилируется, но без синтетических элементов JVM отказалась бы от него, ничего не зная о внутренних классах. Компилятор Java обезуглероживает вышеприведенный класс во что-то вроде следующего:

class Foo {

  private String foo;

  String access$100() {  // synthetic method
    return foo;
  }

  Foo$Bar bar() {
    return new Foo$Bar(this, (Foo$1)null);
  }

  Foo() { } // NON-synthetic, but implicit!
}

class Foo$Bar {

  private final Foo $this; // synthetic field

  private Foo$Bar(Foo $this) {  // synthetic parameter
    this.$this = $this;
  }

  Foo$Bar(Foo $this, Foo$1 unused) {  // synthetic constructor
    this($this);
  }

  String bar() {
    return $this.access$100();
  }
}

class Foo$1 { /*empty, no constructor */ } // synthetic class

Как было сказано, JVM не знает о внутренних классах, но обеспечивает частный доступ членов, т. е. внутренний класс не сможет получить доступ к закрытым свойствам окружающих его классов. Таким образом, компилятору Java необходимо добавить так называемые методы доступа к классу, к которому осуществляется доступ, чтобы раскрыть его невидимые свойства:

  1. Поле foo является закрытым, поэтому доступ к нему возможен только из Foo. Метод access$100 предоставляет это поле своему пакету, в котором всегда можно найти внутренний класс. Этот метод является синтетическим, так как добавляется компилятором.

  2. Конструктор Bar является закрытым и поэтому может быть вызван только из своего собственного класса. Чтобы создать экземпляр Bar, другой (синтетический) конструктор должен раскрыть конструкцию экземпляра. Однако конструкторы имеют фиксированное имя (внутренне все они называются <init>), поэтому мы не можем применить технику для средств доступа к методам, где мы просто назвали их access$xxx. Вместо этого мы делаем методы доступа конструктора уникальными, создавая синтетический тип Foo$1.

  3. Чтобы получить доступ к своему внешнему экземпляру, внутреннему классу необходимо сохранить ссылку на этот экземпляр, которая хранится в синтетическом поле $this. Эта ссылка должна быть передана внутреннему экземпляру с помощью синтетического параметра в конструкторе.

Другими примерами синтетических элементов являются классы, представляющие лямбда-выражения, мостовые методы при переопределении методов с отличающимися по типу сигнатурами, создание Proxy классов или классов, созданных другими инструментами, такими как сборки Maven, или генераторами кода времени выполнения, такими как Byte Buddy (бессовестный плагин).

person Rafael Winterhalter    schedule 26.09.2014

Это «синтетический» флаг, устанавливаемый, когда поле или метод генерируется компилятором. Насколько я знаю, это для внутренних классов, которые соответствуют вашему наблюдению, и должны быть установлены, когда артефакт не появляется в исходном коде.

http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#88571

person Dave Newton    schedule 16.12.2011
comment
говорит: Член класса, которого нет в исходном коде, должен быть помечен синтетическим атрибутом. Означает ли это, что синтетический элемент также помечен модификатором доступа 0x1000u2 access_flags)? Не ясно. - person Peter Kofler; 17.12.2011