в чем разница между capturingClass и implClass для Java SerializedLambda?

Подпись SerializedLambda выглядит следующим образом:

SerializedLambda(класс capturingClass, String functionInterfaceClass, String functionInterfaceMethodName, String functionInterfaceMethodSignature, int implMethodKind, String implClass, String implMethodName, String implMethodSignature, String instantiatedMethodTypeArgs, Object[]

Теперь я вижу, что класс захвата совпадает с deserializeLambda:

SerializedLambda имеет метод readResolve, который ищет (возможно, частный) статический метод с именем $deserializeLambda$(SerializedLambda) в классе захвата, вызывает его с собой в качестве первого аргумента.

Но я не понял implClass это для.

Получите имя класса, содержащего метод реализации.

Разве класс захвата не тот, у которого есть метод реализации? Я попытался сериализовать лямбда-функции, чтобы увидеть, какие значения содержат эти поля, но у них был один и тот же класс. В чем разница междуimplClass а также capturingClass?

1 ответ

Решение

В текущих компиляторах лямбда-выражения всегда компилируются в синтетические методы одного класса, содержащие тело лямбда-выражения. Для этих лямбда-выражений класс захвата всегда идентичен "implClass ".

Но для ссылки на метод, для которой не нужен вспомогательный метод, "implClass "будет классом объявления целевого метода, тогда как класс захвата - это класс, содержащий ссылку на метод.

Например, следующий пример

public class SerializedLambdaExample {
    public static void main(String[] args) throws ReflectiveOperationException {
        var hex = (IntFunction<String>&Serializable)Integer::toHexString;
        Method m = hex.getClass().getDeclaredMethod("writeReplace");
        m.setAccessible(true);
        SerializedLambda sl = (SerializedLambda)m.invoke(hex);
        System.out.println("cap: "+sl.getCapturingClass());
        System.out.println("target: "+sl.getImplClass()+" "+sl.getImplMethodName());
    }
}

печатает с помощью HotSpot/OpenJDK/javac

cap: SerializedLambdaExample
target: java/lang/Integer toHexString

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

Кроме того, не гарантируется, что пример будет работать во всех средах выполнения. Это только для демонстрационных целей.

Другие вопросы по тегам