Когда устанавливается флаг модификатора доступа к байт-коду JVM 0x1000 (hex) «синтетическим»? - PullRequest
9 голосов
/ 17 декабря 2011

Для какого-то проекта парсера байт-кода 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 для.Я видел это однажды во внутреннем классе, но для всех внутренних классов, которые я проверял с тех пор, этот флаг никогда не устанавливался.Теперь вы понимаете, что означает этот флаг и где / когда он установлен?

Ответы [ 2 ]

9 голосов
/ 26 сентября 2014

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

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

class Foo {

  private String foo;

  class Bar {

    private Bar() { }

    String bar() {
      return foo;
    }
  }

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

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

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 (бесстыдная вилка).

3 голосов
/ 17 декабря 2011

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

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

...