All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.lang3.function.MethodInvokers Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.commons.lang3.function;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleProxies;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

import org.apache.commons.lang3.exception.UncheckedIllegalAccessException;

/**
 * Converts {@link Method} objects to lambdas.
 * 

* More specifically, produces instances of single-method interfaces which redirect calls to methods; see * {@link #asInterfaceInstance(Class, Method)}. *

*

Calling supplier methods with no arguments

*

* If the interface's single-method defines no arguments, use {@link #asFunction(Method)} and then apply the function * passing in the object to receive the method call. *

*

* For example to invoke {@link String#length()}: *

* *
 * final Method method = String.class.getMethod("length");
 * final Function<String, Integer> function = MethodInvokers.asFunction(method);
 * assertEquals(3, function.apply("ABC"));
 * 
* *

Calling function methods with one argument

*

* If the interface's single-method defines one argument, use {@link #asBiFunction(Method)} and then apply the function * passing in the object to receive the method call. The second argument to the function is the only argument to the * method. *

*

* For example to invoke {@link String#charAt(int)}: *

* *
 * final Method method = String.class.getMethod("charAt", int.class);
 * final BiFunction<String, Integer, Character> function = MethodInvokers.asBiFunction(method);
 * assertEquals('C', function.apply("ABC", 2));
 * 
* * @since 3.13.0 */ public final class MethodInvokers { /** * Produces a {@link BiConsumer} for a given consumer Method. For example, a classic setter method (as opposed * to a fluent setter). You call the BiConsumer with two arguments: (1) the object receiving the method call, and (2) * the method argument. * * @param the type of the first argument to the operation: The type containing the Method. * @param the type of the second argument to the operation: The type of the method argument. * @param method the method to invoke. * @return a correctly-typed wrapper for the given target. */ @SuppressWarnings("unchecked") public static BiConsumer asBiConsumer(final Method method) { return asInterfaceInstance(BiConsumer.class, method); } /** * Produces a {@link BiFunction} for a given a function Method. You call the BiFunction with two arguments: (1) * the object receiving the method call, and (2) the method argument. The BiFunction return type must match the method's * return type. *

* For example to invoke {@link String#charAt(int)}: *

* *
     * final Method method = String.class.getMethod("charAt", int.class);
     * final BiFunction<String, Integer, Character> function = MethodInvokers.asBiFunction(method);
     * assertEquals('C', function.apply("ABC", 2));
     * 
* * @param the type of the first argument to the function: The type containing the method. * @param the type of the second argument to the function: the method argument type. * @param the type of the result of the function: The method return type. * @param method the method to invoke. * @return a correctly-typed wrapper for the given target. */ @SuppressWarnings("unchecked") public static BiFunction asBiFunction(final Method method) { return asInterfaceInstance(BiFunction.class, method); } /** * Produces a {@link FailableBiConsumer} for a given consumer Method. For example, a classic setter method (as * opposed to a fluent setter). You call the FailableBiConsumer with two arguments: (1) the object receiving the method * call, and (2) the method argument. * * @param the type of the first argument to the operation: The type containing the Method. * @param the type of the second argument to the operation: The type of the method argument. * @param method the method to invoke. * @return a correctly-typed wrapper for the given target. */ @SuppressWarnings("unchecked") public static FailableBiConsumer asFailableBiConsumer(final Method method) { return asInterfaceInstance(FailableBiConsumer.class, method); } /** * Produces a {@link FailableBiFunction} for a given a function Method. You call the FailableBiFunction with * two arguments: (1) the object receiving the method call, and (2) the method argument. The BiFunction return type must * match the method's return type. * * @param the type of the first argument to the function: The type containing the method. * @param the type of the second argument to the function: the method argument type. * @param the type of the result of the function: The method return type. * @param method the method to invoke. * @return a correctly-typed wrapper for the given target. */ @SuppressWarnings("unchecked") public static FailableBiFunction asFailableBiFunction(final Method method) { return asInterfaceInstance(FailableBiFunction.class, method); } /** * Produces a {@link FailableFunction} for a given a supplier Method. You call the Function with one argument: * the object receiving the method call. The FailableFunction return type must match the method's return type. * * @param the type of the first argument to the function: The type containing the method. * @param the type of the result of the function: The method return type. * @param method the method to invoke. * @return a correctly-typed wrapper for the given target. */ @SuppressWarnings("unchecked") public static FailableFunction asFailableFunction(final Method method) { return asInterfaceInstance(FailableFunction.class, method); } /** * Produces a {@link FailableSupplier} for a given a supplier Method. The FailableSupplier return type must * match the method's return type. *

* Only works with static methods. *

* * @param The Method return type. * @param method the method to invoke. * @return a correctly-typed wrapper for the given target. */ @SuppressWarnings("unchecked") public static FailableSupplier asFailableSupplier(final Method method) { return asInterfaceInstance(FailableSupplier.class, method); } /** * Produces a {@link Function} for a given a supplier Method. You call the Function with one argument: the * object receiving the method call. The Function return type must match the method's return type. *

* For example to invoke {@link String#length()}: *

* *
     * final Method method = String.class.getMethod("length");
     * final Function<String, Integer> function = MethodInvokers.asFunction(method);
     * assertEquals(3, function.apply("ABC"));
     * 
* * @param the type of the first argument to the function: The type containing the method. * @param the type of the result of the function: The method return type. * @param method the method to invoke. * @return a correctly-typed wrapper for the given target. */ @SuppressWarnings("unchecked") public static Function asFunction(final Method method) { return asInterfaceInstance(Function.class, method); } /** * Produces an instance of the given single-method interface which redirects its calls to the given method. *

* For the definition of "single-method", see {@linkplain MethodHandleProxies#asInterfaceInstance(Class, MethodHandle)}. *

* * @param The interface type. * @param interfaceClass a class object representing {@code T}. * @param method the method to invoke. * @return a correctly-typed wrapper for the given target. * @see MethodHandleProxies#asInterfaceInstance(Class, MethodHandle) */ public static T asInterfaceInstance(final Class interfaceClass, final Method method) { return MethodHandleProxies.asInterfaceInstance(Objects.requireNonNull(interfaceClass, "interfaceClass"), unreflectUnchecked(method)); } /** * Produces a {@link Supplier} for a given a supplier Method. The Supplier return type must match the method's * return type. *

* Only works with static methods. *

* * @param The Method return type. * @param method the method to invoke. * @return a correctly-typed wrapper for the given target. */ @SuppressWarnings("unchecked") public static Supplier asSupplier(final Method method) { return asInterfaceInstance(Supplier.class, method); } /** * Throws NullPointerException if {@code method} is {@code null}. * * @param method The method to test. * @return The given method. * @throws NullPointerException if {@code method} is {@code null}. */ private static Method requireMethod(final Method method) { return Objects.requireNonNull(method, "method"); } private static MethodHandle unreflect(final Method method) throws IllegalAccessException { return MethodHandles.lookup().unreflect(requireMethod(method)); } private static MethodHandle unreflectUnchecked(final Method method) { try { return unreflect(method); } catch (final IllegalAccessException e) { throw new UncheckedIllegalAccessException(e); } } /** * No need to create instances. */ private MethodInvokers() { // noop } }