Skip to content

Instantly share code, notes, and snippets.

@keturn
Last active May 18, 2022 03:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save keturn/180a57f2f6069470556137bd06b4025d to your computer and use it in GitHub Desktop.
Save keturn/180a57f2f6069470556137bd06b4025d to your computer and use it in GitHub Desktop.
Java: Get a lambda's parameter types
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.invoke.MethodType;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.List;
class SpyingOutputObjectStream extends ObjectOutputStream {
private final List<Object> emittedObjects = new ArrayList<>();
SpyingOutputObjectStream(OutputStream out) throws IOException {
super(out);
enableReplaceObject(true);
}
@Override
protected Object replaceObject(Object obj) {
emittedObjects.add(obj);
return null;
}
List<Object> getEmittedObjects() {
return List.copyOf(emittedObjects);
}
/**
* This technique, obtuse as it is, does not require overriding anything marked private.
*/
static SerializedLambda getSerializedLambdaFromOutputStream(Serializable obj) {
List<Object> outputObjects;
try (var output = new SpyingOutputObjectStream(new ByteArrayOutputStream())) {
try {
output.writeObject(obj);
} finally {
outputObjects = output.getEmittedObjects();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return (SerializedLambda) outputObjects.get(0);
}
/**
* This relies on {@link Method#setAccessible} on a private final field.
* <p>
* Is that bad? You might think so, but {@link Serializable} documentation says that
* Serializable objects may have that method <em>even though</em> it is not defined in
* the interface. So it's probably okay?
*/
static SerializedLambda getSerializedLambdaDirectly(Serializable obj) {
Method m;
try {
m = obj.getClass().getDeclaredMethod("writeReplace");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
try {
PrivilegedExceptionAction<?> act = () -> {
m.setAccessible(true);
return m.invoke(obj);
};
Object replacement = AccessController.doPrivileged(act);
return (SerializedLambda) replacement;
} catch (PrivilegedActionException e) {
throw new RuntimeException(e);
}
}
static List<Class<?>> getLambdaParameters(SerializedLambda serializedLambda) {
var methodType = MethodType.fromMethodDescriptorString(
serializedLambda.getImplMethodSignature(),
serializedLambda.getClass().getClassLoader()
);
return methodType.parameterList();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment