Что означает $$ в имени, сгенерированном javac?

При переходе через граф вызовов java, созданный такими библиотеками, как DependencyFinder и java-callgraph, я обнаружил, что компилятор java генерирует имена для анонимных функций, внутренних классов и т. д.

Я узнал значение пары из них (пожалуйста, поправьте, если я ошибаюсь):

  • org.example.Bar$Foo относится к Foo, который является внутренним классом org.example.Bar.
  • org.example.Bar$1 относится к анонимному классу, объявленному внутри одного из методов org.example.Bar.
  • org.example.Bar.lambda$spam$1() относится к лямбде, объявленной внутри метода org.example.Bar.spam().

Однако я также нашел:

  1. org.example.Bar$$Lambda$2.args$1
  2. org.example.Bar$$Lambda$2.call()
  3. org.example.Bar$$Lambda$7.lambdaFactory$()
  4. org.example.Bar$$Lambda$7.get$Lambda()

Что означают четыре имени выше? Что означает двойной доллар ($$)?


person rickchristie    schedule 17.03.2016    source источник
comment
Это просто разделитель, это может быть что угодно.   -  person biziclop    schedule 17.03.2016
comment
Кажется очевидным, что двойной $$ просто используется для специальных новых классов, используемых для лямбда-выражений. Таким образом, $ используется для разделения имен внутренних классов, а $$ отмечает внутренние классы, основанные на лямбда-выражениях.   -  person GhostCat    schedule 17.03.2016
comment
Обновление: я нашел ссылку в спецификации: docs.oracle.com/ javase/specs/jls/se8/jls8.pdf — чтение.   -  person rickchristie    schedule 17.03.2016


Ответы (1)


Классы для лямбда-выражений не javac генерируются, а создаются JRE во время выполнения. Их имена совершенно не указаны, и вы не можете полагаться на какую-либо схему именования.

Но очевидно, что текущая JRE Oracle имеет узнаваемый шаблон. Он добавляет $$Lambda$n к имени определяющего класса, тогда как n — это возрастающее число, которое отражает порядок создания во время выполнения, а не какое-либо свойство скомпилированного кода.

Вы можете проверить это с помощью следующей программы:

public class Test {
    public static void main(String... args) {
        if(args.length==0) {
            final boolean meFirst = Math.random()<0.5;
            if(meFirst) {
                Runnable r=Test::main;
                System.out.println("first run:\t"+r.getClass());
            }
            main("second run");
            if(!meFirst) {
                Runnable r=Test::main;
                System.out.println("first run:\t"+r.getClass());
            }
        }
        else {
            Runnable r=Test::main;
            System.out.println(args[0]+":\t"+r.getClass());
            if(args[0].equals("second run")) main("last run");
        }
    }
}

В зависимости от состояния случайного флага meFirst он будет печатать либо

first run:  class Test$$Lambda$1
second run: class Test$$Lambda$2
last run:   class Test$$Lambda$2

or

second run: class Test$$Lambda$1
last run:   class Test$$Lambda$1
first run:  class Test$$Lambda$2

Он показывает, что первый сгенерированный класс всегда получает номер 1, независимо от того, является ли он одной из первых двух ссылок на метод, созданных в первом вызове main, или третьей ссылкой на метод, созданной в первой рекурсии. Кроме того, третье выполнение всегда сталкивается с тем же классом, что и второе, поскольку это одно и то же выражение ссылки на метод (примечание: разные выражения, поскольку цель всех выражений одна и та же), и класс переопределяется. использовал.

В зависимости от версии вы можете увидеть что-то вроде /number, добавленное к именам, что намекает на то, что имена действительно не имеют значения, поскольку каждый из этих классов имеет другой уникальный идентификатор (это так называемые «анонимные классы», которые вы можете найти через ClassLoader и имя).

Имена полей, такие как args$n в этих классах, представляют n-е захваченное значение. Поскольку LambdaMetafactory ничего не знает относительно реальных имен захваченных переменных, у него нет другого выбора, кроме как генерировать такие имена.


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

person Holger    schedule 02.06.2016