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