< prev index next >
src/java.base/share/classes/java/lang/invoke/LambdaMetafactory.java
Print this page
rev 54208 : 8174222: LambdaMetafactory: validate inputs and improve documentation
@@ -25,10 +25,12 @@
package java.lang.invoke;
import java.io.Serializable;
import java.util.Arrays;
+import java.lang.reflect.Array;
+import java.util.Objects;
/**
* <p>Methods to facilitate the creation of simple "function objects" that
* implement one or more interfaces by delegation to a provided {@link MethodHandle},
* possibly after type adaptation and partial evaluation of arguments. These
@@ -37,53 +39,59 @@
* reference expression</em> features of the Java Programming Language.
*
* <p>Indirect access to the behavior specified by the provided {@code MethodHandle}
* proceeds in order through three phases:
* <ul>
- * <li><em>Linkage</em> occurs when the methods in this class are invoked.
+ * <li><p><em>Linkage</em> occurs when the methods in this class are invoked.
* They take as arguments an interface to be implemented (typically a
* <em>functional interface</em>, one with a single abstract method), a
* name and signature of a method from that interface to be implemented, a
- * method handle describing the desired implementation behavior
- * for that method, and possibly other additional metadata, and produce a
- * {@link CallSite} whose target can be used to create suitable function
- * objects. Linkage may involve dynamically loading a new class that
- * implements the target interface. The {@code CallSite} can be considered a
- * "factory" for function objects and so these linkage methods are referred
- * to as "metafactories".</li>
+ * {@link MethodHandleInfo direct method handle} describing the desired
+ * implementation behavior for that method, and possibly other additional
+ * metadata, and produce a {@link CallSite} whose target can be used to
+ * create suitable function objects.
+ *
+ * <p>Linkage may involve dynamically loading a new class that implements
+ * the target interface, or re-using a suitable existing class.
+ *
+ * <p>The {@code CallSite} can be considered a "factory" for function
+ * objects and so these linkage methods are referred to as
+ * "metafactories".</li>
*
- * <li><em>Capture</em> occurs when the {@code CallSite}'s target is
+ * <li><p><em>Capture</em> occurs when the {@code CallSite}'s target is
* invoked, typically through an {@code invokedynamic} call site,
* producing a function object. This may occur many times for
- * a single factory {@code CallSite}. Capture may involve allocation of a
- * new function object, or may return an existing function object. The
- * behavior {@code MethodHandle} may have additional parameters beyond those
- * of the specified interface method; these are referred to as <em>captured
- * parameters</em>, which must be provided as arguments to the
- * {@code CallSite} target, and which may be early-bound to the behavior
- * {@code MethodHandle}. The number of captured parameters and their types
- * are determined during linkage.
- * The identity of a function object produced by invoking the
- * {@code CallSite}'s target is unpredictable, and therefore
- * identity-sensitive operations (such as reference equality, object
- * locking, and {@code System.identityHashCode()} may produce different
- * results in different implementations, or even upon different invocations
- * in the same implementation.</li>
- *
- * <li><em>Invocation</em> occurs when an implemented interface method
- * is invoked on a function object. This may occur many times for a single
- * function object. The method referenced by the behavior {@code MethodHandle}
- * is invoked with the captured arguments and any additional arguments
- * provided on invocation, as if by {@link MethodHandle#invoke(Object...)}.</li>
+ * a single factory {@code CallSite}.
+ *
+ * <p>If the behavior {@code MethodHandle} has additional parameters beyond
+ * those of the specified interface method, these are referred to as
+ * <em>captured parameters</em>, which must be provided as arguments to the
+ * {@code CallSite} target. The expected number and types of captured
+ * parameters are determined during linkage.
+ *
+ * <p>Capture may involve allocation of a new function object, or may return
+ * a suitable existing function object. The identity of a function object
+ * produced by capture is unpredictable, and therefore identity-sensitive
+ * operations (such as reference equality, object locking, and {@code
+ * System.identityHashCode()}) may produce different results in different
+ * implementations, or even upon different invocations in the same
+ * implementation.</li>
+ *
+ * <li><p><em>Invocation</em> occurs when an implemented interface method is
+ * invoked on a function object. This may occur many times for a single
+ * function object. The method referenced by the implementation MethodHandle
+ * is invoked, passing to it the captured arguments and the invocation
+ * arguments. The result of the method is returned.
+ * </li>
* </ul>
*
* <p>It is sometimes useful to restrict the set of inputs or results permitted
* at invocation. For example, when the generic interface {@code Predicate<T>}
* is parameterized as {@code Predicate<String>}, the input must be a
* {@code String}, even though the method to implement allows any {@code Object}.
* At linkage time, an additional {@link MethodType} parameter describes the
- * "instantiated" method type; on invocation, the arguments and eventual result
+ * "dynamic" method type; on invocation, the arguments and eventual result
* are checked against this {@code MethodType}.
*
* <p>This class provides two forms of linkage methods: a standard version
* ({@link #metafactory(MethodHandles.Lookup, String, MethodType, MethodType, MethodHandle, MethodType)})
* using an optimized protocol, and an alternate version
@@ -119,28 +127,26 @@
* {@link SerializedLambda} for details.</li>
* </ul>
*
* <p>Assume the linkage arguments are as follows:
* <ul>
- * <li>{@code invokedType} (describing the {@code CallSite} signature) has
+ * <li>{@code factoryType} (describing the {@code CallSite} signature) has
* K parameters of types (D1..Dk) and return type Rd;</li>
- * <li>{@code samMethodType} (describing the implemented method type) has N
+ * <li>{@code interfaceMethodType} (describing the implemented method type) has N
* parameters, of types (U1..Un) and return type Ru;</li>
- * <li>{@code implMethod} (the {@code MethodHandle} providing the
- * implementation has M parameters, of types (A1..Am) and return type Ra
+ * <li>{@code implementation} (the {@code MethodHandle} providing the
+ * implementation) has M parameters, of types (A1..Am) and return type Ra
* (if the method describes an instance method, the method type of this
* method handle already includes an extra first argument corresponding to
* the receiver);</li>
- * <li>{@code instantiatedMethodType} (allowing restrictions on invocation)
+ * <li>{@code dynamicMethodType} (allowing restrictions on invocation)
* has N parameters, of types (T1..Tn) and return type Rt.</li>
* </ul>
*
* <p>Then the following linkage invariants must hold:
* <ul>
- * <li>Rd is an interface</li>
- * <li>{@code implMethod} is a <em>direct method handle</em></li>
- * <li>{@code samMethodType} and {@code instantiatedMethodType} have the same
+ * <li>{@code interfaceMethodType} and {@code dynamicMethodType} have the same
* arity N, and for i=1..N, Ti and Ui are the same type, or Ti and Ui are
* both reference types and Ti is a subtype of Ui</li>
* <li>Either Rt and Ru are the same type, or both are reference types and
* Rt is a subtype of Ru</li>
* <li>K + N = M</li>
@@ -148,11 +154,11 @@
* <li>For i=1..N, Ti is adaptable to Aj, where j=i+k</li>
* <li>The return type Rt is void, or the return type Ra is not void and is
* adaptable to Rt</li>
* </ul>
*
- * <p>Further, at capture time, if {@code implMethod} corresponds to an instance
+ * <p>Further, at capture time, if {@code implementation} corresponds to an instance
* method, and there are any capture arguments ({@code K > 0}), then the first
* capture argument (corresponding to the receiver) must be non-null.
*
* <p>A type Q is considered adaptable to S as follows:
* <table class="striped">
@@ -216,28 +222,28 @@
* (which in this case, includes information describing the implementation method,
* the target interface, and the target interface method(s)), as well as a
* method signature describing the number and static types (but not the values)
* of the dynamic arguments and the static return type of the invokedynamic site.
*
- * @implNote The implementation method is described with a method handle. In
- * theory, any method handle could be used. Currently supported are direct method
- * handles representing invocation of virtual, interface, constructor and static
- * methods.
+ * <p>The implementation method is described with a direct method handle
+ * referencing a method or constructor. In theory, any method handle could be
+ * used, but this is not compatible with some implementation techniques and
+ * would complicate the work implementations must do.
+ *
* @since 1.8
*/
public final class LambdaMetafactory {
private LambdaMetafactory() {}
- /** Flag for alternate metafactories indicating the lambda object
+ /** Flag for {@link #altMetafactory} indicating the lambda object
* must be serializable */
public static final int FLAG_SERIALIZABLE = 1 << 0;
/**
- * Flag for alternate metafactories indicating the lambda object implements
- * other marker interfaces
- * besides Serializable
+ * Flag for {@link #altMetafactory} indicating the lambda object implements
+ * other interfaces besides {@code Serializable}
*/
public static final int FLAG_MARKERS = 1 << 1;
/**
* Flag for alternate metafactories indicating the lambda object requires
@@ -247,11 +253,11 @@
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
private static final MethodType[] EMPTY_MT_ARRAY = new MethodType[0];
// LambdaMetafactory bootstrap methods are startup sensitive, and may be
- // special cased in java.lang.invokeBootstrapMethodInvoker to ensure
+ // special cased in java.lang.invoke.BootstrapMethodInvoker to ensure
// methods are invoked with exact type information to avoid generating
// code for runtime checks. Take care any changes or additions here are
// reflected there as appropriate.
/**
@@ -267,66 +273,73 @@
* A general description of the behavior of this method is provided
* {@link LambdaMetafactory above}.
*
* <p>When the target of the {@code CallSite} returned from this method is
* invoked, the resulting function objects are instances of a class which
- * implements the interface named by the return type of {@code invokedType},
- * declares a method with the name given by {@code invokedName} and the
- * signature given by {@code samMethodType}. It may also override additional
+ * implements the interface named by the return type of {@code factoryType},
+ * declares a method with the name given by {@code methodName} and the
+ * signature given by {@code interfaceMethodType}. It may also override additional
* methods from {@code Object}.
*
* @param caller Represents a lookup context with the accessibility
* privileges of the caller. Specifically, the lookup context
* must have
* <a href="MethodHandles.Lookup.html#privacc">private access</a>
* privileges.
* When used with {@code invokedynamic}, this is stacked
* automatically by the VM.
- * @param invokedName The name of the method to implement. When used with
+ * @param methodName The name of the method to implement. When used with
* {@code invokedynamic}, this is provided by the
* {@code NameAndType} of the {@code InvokeDynamic}
* structure and is stacked automatically by the VM.
- * @param invokedType The expected signature of the {@code CallSite}. The
+ * @param factoryType The expected signature of the {@code CallSite}. The
* parameter types represent the types of capture variables;
* the return type is the interface to implement. When
* used with {@code invokedynamic}, this is provided by
* the {@code NameAndType} of the {@code InvokeDynamic}
* structure and is stacked automatically by the VM.
- * In the event that the implementation method is an
- * instance method and this signature has any parameters,
- * the first parameter in the invocation signature must
- * correspond to the receiver.
- * @param samMethodType Signature and return type of method to be implemented
- * by the function object.
- * @param implMethod A direct method handle describing the implementation
+ * @param interfaceMethodType Signature and return type of method to be
+ * implemented by the function object.
+ * @param implementation A direct method handle describing the implementation
* method which should be called (with suitable adaptation
- * of argument types, return types, and with captured
+ * of argument types and return types, and with captured
* arguments prepended to the invocation arguments) at
* invocation time.
- * @param instantiatedMethodType The signature and return type that should
+ * @param dynamicMethodType The signature and return type that should
* be enforced dynamically at invocation time.
- * This may be the same as {@code samMethodType},
- * or may be a specialization of it.
+ * In simple use cases this is the same as
+ * {@code interfaceMethodType}.
* @return a CallSite whose target can be used to perform capture, generating
- * instances of the interface named by {@code invokedType}
- * @throws LambdaConversionException If any of the linkage invariants
- * described {@link LambdaMetafactory above}
- * are violated, or the lookup context
- * does not have private access privileges.
+ * instances of the interface named by {@code factoryType}
+ * @throws LambdaConversionException If {@code caller} does not have private
+ * access privileges, or if {@code methodName} is not a valid JVM
+ * method name, or if the return type of {@code factoryType} is not
+ * an interface, or if {@code implementation} is not a direct method
+ * handle referencing a method or constructor, or if the linkage
+ * invariants are violated, as defined {@link LambdaMetafactory above}.
+ * @throws NullPointerException If any argument is {@code null}.
+ * @throws SecurityException If a security manager is present, and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * from {@code caller} to the package of {@code implementation}.
*/
public static CallSite metafactory(MethodHandles.Lookup caller,
- String invokedName,
- MethodType invokedType,
- MethodType samMethodType,
- MethodHandle implMethod,
- MethodType instantiatedMethodType)
+ String methodName,
+ MethodType factoryType,
+ MethodType interfaceMethodType,
+ MethodHandle implementation,
+ MethodType dynamicMethodType)
throws LambdaConversionException {
AbstractValidatingLambdaMetafactory mf;
- mf = new InnerClassLambdaMetafactory(caller, invokedType,
- invokedName, samMethodType,
- implMethod, instantiatedMethodType,
- false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
+ mf = new InnerClassLambdaMetafactory(Objects.requireNonNull(caller),
+ Objects.requireNonNull(factoryType),
+ Objects.requireNonNull(methodName),
+ Objects.requireNonNull(interfaceMethodType),
+ Objects.requireNonNull(implementation),
+ Objects.requireNonNull(dynamicMethodType),
+ false,
+ EMPTY_CLASS_ARRAY,
+ EMPTY_MT_ARRAY);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}
/**
@@ -349,27 +362,27 @@
* parameter that contains additional parameters. The declared argument
* list for this method is:
*
* <pre>{@code
* CallSite altMetafactory(MethodHandles.Lookup caller,
- * String invokedName,
- * MethodType invokedType,
+ * String methodName,
+ * MethodType factoryType,
* Object... args)
* }</pre>
*
* <p>but it behaves as if the argument list is as follows:
*
* <pre>{@code
* CallSite altMetafactory(MethodHandles.Lookup caller,
- * String invokedName,
- * MethodType invokedType,
- * MethodType samMethodType,
- * MethodHandle implMethod,
- * MethodType instantiatedMethodType,
+ * String methodName,
+ * MethodType factoryType,
+ * MethodType interfaceMethodType,
+ * MethodHandle implementation,
+ * MethodType dynamicMethodType,
* int flags,
- * int markerInterfaceCount, // IF flags has MARKERS set
- * Class... markerInterfaces, // IF flags has MARKERS set
+ * int interfaceCount, // IF flags has MARKERS set
+ * Class... interfaces, // IF flags has MARKERS set
* int bridgeCount, // IF flags has BRIDGES set
* MethodType... bridges // IF flags has BRIDGES set
* )
* }</pre>
*
@@ -379,29 +392,29 @@
* are interpreted as follows:
* <ul>
* <li>{@code flags} indicates additional options; this is a bitwise
* OR of desired flags. Defined flags are {@link #FLAG_BRIDGES},
* {@link #FLAG_MARKERS}, and {@link #FLAG_SERIALIZABLE}.</li>
- * <li>{@code markerInterfaceCount} is the number of additional interfaces
+ * <li>{@code interfaceCount} is the number of additional interfaces
* the function object should implement, and is present if and only if the
* {@code FLAG_MARKERS} flag is set.</li>
- * <li>{@code markerInterfaces} is a variable-length list of additional
- * interfaces to implement, whose length equals {@code markerInterfaceCount},
+ * <li>{@code interfaces} is a variable-length list of additional
+ * interfaces to implement, whose length equals {@code interfaceCount},
* and is present if and only if the {@code FLAG_MARKERS} flag is set.</li>
* <li>{@code bridgeCount} is the number of additional method signatures
* the function object should implement, and is present if and only if
* the {@code FLAG_BRIDGES} flag is set.</li>
* <li>{@code bridges} is a variable-length list of additional
* methods signatures to implement, whose length equals {@code bridgeCount},
* and is present if and only if the {@code FLAG_BRIDGES} flag is set.</li>
* </ul>
*
- * <p>Each class named by {@code markerInterfaces} is subject to the same
- * restrictions as {@code Rd}, the return type of {@code invokedType},
+ * <p>Each class named by {@code interfaces} is subject to the same
+ * restrictions as {@code Rd}, the return type of {@code factoryType},
* as described {@link LambdaMetafactory above}. Each {@code MethodType}
* named by {@code bridges} is subject to the same restrictions as
- * {@code samMethodType}, as described {@link LambdaMetafactory above}.
+ * {@code interfaceMethodType}, as described {@link LambdaMetafactory above}.
*
* <p>When FLAG_SERIALIZABLE is set in {@code flags}, the function objects
* will implement {@code Serializable}, and will have a {@code writeReplace}
* method that returns an appropriate {@link SerializedLambda}. The
* {@code caller} class must have an appropriate {@code $deserializeLambda$}
@@ -410,13 +423,13 @@
* <p>When the target of the {@code CallSite} returned from this method is
* invoked, the resulting function objects are instances of a class with
* the following properties:
* <ul>
* <li>The class implements the interface named by the return type
- * of {@code invokedType} and any interfaces named by {@code markerInterfaces}</li>
- * <li>The class declares methods with the name given by {@code invokedName},
- * and the signature given by {@code samMethodType} and additional signatures
+ * of {@code factoryType} and any interfaces named by {@code interfaces}</li>
+ * <li>The class declares methods with the name given by {@code methodName},
+ * and the signature given by {@code interfaceMethodType} and additional signatures
* given by {@code bridges}</li>
* <li>The class may override methods from {@code Object}, and may
* implement methods related to serialization.</li>
* </ul>
*
@@ -425,82 +438,121 @@
* must have
* <a href="MethodHandles.Lookup.html#privacc">private access</a>
* privileges.
* When used with {@code invokedynamic}, this is stacked
* automatically by the VM.
- * @param invokedName The name of the method to implement. When used with
+ * @param methodName The name of the method to implement. When used with
* {@code invokedynamic}, this is provided by the
* {@code NameAndType} of the {@code InvokeDynamic}
* structure and is stacked automatically by the VM.
- * @param invokedType The expected signature of the {@code CallSite}. The
+ * @param factoryType The expected signature of the {@code CallSite}. The
* parameter types represent the types of capture variables;
* the return type is the interface to implement. When
* used with {@code invokedynamic}, this is provided by
* the {@code NameAndType} of the {@code InvokeDynamic}
* structure and is stacked automatically by the VM.
- * In the event that the implementation method is an
- * instance method and this signature has any parameters,
- * the first parameter in the invocation signature must
- * correspond to the receiver.
- * @param args An {@code Object[]} array containing the required
- * arguments {@code samMethodType}, {@code implMethod},
- * {@code instantiatedMethodType}, {@code flags}, and any
- * optional arguments, as described
- * {@link #altMetafactory(MethodHandles.Lookup, String, MethodType, Object...)} above}
+ * @param args An array of {@code Object} containing the required
+ * arguments {@code interfaceMethodType}, {@code implementation},
+ * {@code dynamicMethodType}, {@code flags}, and any
+ * optional arguments, as described above
* @return a CallSite whose target can be used to perform capture, generating
- * instances of the interface named by {@code invokedType}
- * @throws LambdaConversionException If any of the linkage invariants
- * described {@link LambdaMetafactory above}
- * are violated, or the lookup context
- * does not have private access privileges.
+ * instances of the interface named by {@code factoryType}
+ * @throws LambdaConversionException If {@code caller} does not have private
+ * access privileges, or if {@code methodName} is not a valid JVM
+ * method name, or if the return type of {@code factoryType} is not
+ * an interface, or if any of {@code interfaces} is not an
+ * interface, or if {@code implementation} is not a direct method
+ * handle referencing a method or constructor, or if the linkage
+ * invariants are violated, as defined {@link LambdaMetafactory above}.
+ * @throws NullPointerException If any argument, or any component of {@code args},
+ * is {@code null}.
+ * @throws IllegalArgumentException If the number or types of the components
+ * of {@code args} do not follow the above rules, or if
+ * {@code interfaceCount} or {@code bridgeCount} are negative
+ * integers.
+ * @throws SecurityException If a security manager is present, and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * from {@code caller} to the package of {@code implementation}.
*/
public static CallSite altMetafactory(MethodHandles.Lookup caller,
- String invokedName,
- MethodType invokedType,
+ String methodName,
+ MethodType factoryType,
Object... args)
throws LambdaConversionException {
- MethodType samMethodType = (MethodType)args[0];
- MethodHandle implMethod = (MethodHandle)args[1];
- MethodType instantiatedMethodType = (MethodType)args[2];
- int flags = (Integer) args[3];
- Class<?>[] markerInterfaces;
- MethodType[] bridges;
- int argIndex = 4;
+ Objects.requireNonNull(caller);
+ Objects.requireNonNull(methodName);
+ Objects.requireNonNull(factoryType);
+ Objects.requireNonNull(args);
+ int argIndex = 0;
+ MethodType interfaceMethodType = extractArg(args, argIndex++, MethodType.class);
+ MethodHandle implementation = extractArg(args, argIndex++, MethodHandle.class);
+ MethodType dynamicMethodType = extractArg(args, argIndex++, MethodType.class);
+ int flags = extractArg(args, argIndex++, Integer.class);
+ Class<?>[] interfaces = EMPTY_CLASS_ARRAY;
+ MethodType[] bridges = EMPTY_MT_ARRAY;
if ((flags & FLAG_MARKERS) != 0) {
- int markerCount = (Integer) args[argIndex++];
- markerInterfaces = new Class<?>[markerCount];
- System.arraycopy(args, argIndex, markerInterfaces, 0, markerCount);
- argIndex += markerCount;
+ int interfaceCount = extractArg(args, argIndex++, Integer.class);
+ if (interfaceCount < 0) {
+ throw new IllegalArgumentException("negative argument count");
+ }
+ if (interfaceCount > 0) {
+ interfaces = extractArgs(args, argIndex, Class.class, interfaceCount);
+ argIndex += interfaceCount;
+ }
}
- else
- markerInterfaces = EMPTY_CLASS_ARRAY;
if ((flags & FLAG_BRIDGES) != 0) {
- int bridgeCount = (Integer) args[argIndex++];
- bridges = new MethodType[bridgeCount];
- System.arraycopy(args, argIndex, bridges, 0, bridgeCount);
+ int bridgeCount = extractArg(args, argIndex++, Integer.class);
+ if (bridgeCount < 0) {
+ throw new IllegalArgumentException("negative argument count");
+ }
+ if (bridgeCount > 0) {
+ bridges = extractArgs(args, argIndex, MethodType.class, bridgeCount);
argIndex += bridgeCount;
}
- else
- bridges = EMPTY_MT_ARRAY;
+ }
+ if (argIndex < args.length) {
+ throw new IllegalArgumentException("too many arguments");
+ }
boolean isSerializable = ((flags & FLAG_SERIALIZABLE) != 0);
if (isSerializable) {
- boolean foundSerializableSupertype = Serializable.class.isAssignableFrom(invokedType.returnType());
- for (Class<?> c : markerInterfaces)
+ boolean foundSerializableSupertype = Serializable.class.isAssignableFrom(factoryType.returnType());
+ for (Class<?> c : interfaces)
foundSerializableSupertype |= Serializable.class.isAssignableFrom(c);
if (!foundSerializableSupertype) {
- markerInterfaces = Arrays.copyOf(markerInterfaces, markerInterfaces.length + 1);
- markerInterfaces[markerInterfaces.length-1] = Serializable.class;
+ interfaces = Arrays.copyOf(interfaces, interfaces.length + 1);
+ interfaces[interfaces.length-1] = Serializable.class;
}
}
AbstractValidatingLambdaMetafactory mf
- = new InnerClassLambdaMetafactory(caller, invokedType,
- invokedName, samMethodType,
- implMethod,
- instantiatedMethodType,
+ = new InnerClassLambdaMetafactory(caller, factoryType,
+ methodName, interfaceMethodType,
+ implementation,
+ dynamicMethodType,
isSerializable,
- markerInterfaces, bridges);
+ interfaces, bridges);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}
+
+ private static <T> T extractArg(Object[] args, int index, Class<T> type) {
+ if (index >= args.length) {
+ throw new IllegalArgumentException("missing argument");
+ }
+ Object result = Objects.requireNonNull(args[index]);
+ if (!type.isInstance(result)) {
+ throw new IllegalArgumentException("argument has wrong type");
+ }
+ return type.cast(result);
+ }
+
+ private static <T> T[] extractArgs(Object[] args, int index, Class<T> type, int count) {
+ @SuppressWarnings("unchecked")
+ T[] result = (T[]) Array.newInstance(type, count);
+ for (int i = 0; i < count; i++) {
+ result[i] = extractArg(args, index + i, type);
+ }
+ return result;
+ }
+
}
< prev index next >