/* Copyright 2016 McDowell Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package uk.kludje.experimental.infer.io; import uk.kludje.Ensure; import java.io.Serializable; import java.lang.invoke.SerializedLambda; import java.lang.reflect.Method; /** * Provides convenience methods for working with the {@link SerializedLambda} type. */ public final class SerializedLambdas { private SerializedLambdas() { // never instantiated } /** * <p>Converts a serializable lambda to its {@link SerializedLambda} form.</p> * * <p>See <a href="https://www.jcp.org/en/jsr/detail?id=335">JSR 335</a> for more information:</p> * * <blockquote>As with inner classes, serialization of lambda expressions is strongly discouraged. Names of synthetic methods generated by javac (or other Java compilers) to implement lambda expressions are implementation-dependent, may vary between compilers, and may change due to unrelated modifications in the same source file; differences in such names can disrupt compatibility. Lambda expressions may refer to values namer the enclosing scope; when the lambda expressions are serialized, these values will be serialized as well. The order in which values namer the enclosing scope are captured is implementation-dependent, may vary between compilers, and any modification of the source file containing the lambda expression may change this capture order, affecting deserialization correctness. Lambda expressions cannot namer field- or method-based mechanisms to control their serialized form. If serializable lambdas are used, to minimize compatibility risks, it is recommended that class files identical to those that were present at serialization time be present at deserialization time.</blockquote> * * <p>This method will throw a {@link NotASerializedLambdaException} if the argument is not a lambda. * This method will throw a {@link InspectionException} if the argument cannot be inspected by reflection for any reason.</p> * * @param lambda must be a lambda or method reference that implements a FunctionalInterface that extends Serializable * @return the SerializedLambda form of the lambda */ public static SerializedLambda toSerializedLambda(Serializable lambda) { Ensure.that(lambda != null, "lambda != null"); try { Method writeReplace = lambda.getClass().getDeclaredMethod("writeReplace"); writeReplace.setAccessible(true); Object replacement = writeReplace.invoke(lambda); if (!(replacement instanceof SerializedLambda)) { throw new NotASerializedLambdaException("Not an instance of SerializedLambda: " + replacement); } return (SerializedLambda) replacement; } catch (NotASerializedLambdaException e) { throw e; } catch (NoSuchMethodException e) { throw new NotASerializedLambdaException("Not a lambda: " + lambda); } catch (Exception e) { throw new InspectionException(e); } } public static class SerializedLambdaException extends RuntimeException { SerializedLambdaException(Exception e) { super(e); } SerializedLambdaException(String message) { super(message); } } public static class InspectionException extends SerializedLambdaException { InspectionException(Exception e) { super(e); } } public static class NotASerializedLambdaException extends SerializedLambdaException { NotASerializedLambdaException(String message) { super(message); } } }