code: public static Tuple2<ClassNode[], ClassNode> parameterizeSAM(ClassNode sam) {
          MethodNode methodNode = ClassHelper.findSAM(sam);
          final Map<GenericsType, GenericsType> map = makeDeclaringAndActualGenericsTypeMapOfExactType(methodNode.getDeclaringClass(), sam);
  
          ClassNode[] parameterTypes =
                  Arrays.stream(methodNode.getParameters())
                      .map(e -> {
                          ClassNode originalParameterType = e.getType();
                          return originalParameterType.isGenericsPlaceHolder()
                                  ? findActualTypeByGenericsPlaceholderName(originalParameterType.getUnresolvedName(), map)
                                  : originalParameterType;
                      })
                      .toArray(ClassNode[]::new);
  
          ClassNode originalReturnType = methodNode.getReturnType();
          ClassNode returnType =
                  originalReturnType.isGenericsPlaceHolder()
                          ? findActualTypeByGenericsPlaceholderName(originalReturnType.getUnresolvedName(), map)
                          : originalReturnType;
  
          return tuple(parameterTypes, returnType);
      }
docs: Get the parameter and return types of the abstract method of SAM
  
  If the abstract method is not parameterized, we will get generics placeholders, e.g. T, U
  For example, the abstract method of {@link java.util.function.Function} is
  <pre>
  R apply(T t);
  </pre>
  
  We parameterize the above interface as {@code Function<String, Integer>}, then the abstract method will be
  <pre>
  Integer apply(String t);
  </pre>
  
  When we call {@code parameterizeSAM} on the ClassNode {@code Function<String, Integer>},
  we can get parameter types and return type of the above abstract method,
  i.e. ClassNode {@code ClassHelper.STRING_TYPE} and {@code ClassHelper.Integer_TYPE}
  
  @param sam the class node which contains only one abstract method
  @return the parameter and return types
  @since 3.0.0