Lambdas are purely a call-site construct: the recipient of the lambda does not need to know that a Lambda is involved, instead it accepts an Interface with the appropriate method.
In other words, you define or use a functional interface (i.e. an interface with a single method) that accepts and returns exactly what you want.
Since Java 8 there is a set of commonly-used interface types in java.util.function.
For this specific use case there's java.util.function.IntBinaryOperator with a single int applyAsInt(int left, int right) method, so you could write your method like this:
static int method(IntBinaryOperator op){
return op.applyAsInt(5, 10);
}
But you can just as well define your own interface and use it like this:
public interface TwoArgIntOperator {
public int op(int a, int b);
}
//elsewhere:
static int method(TwoArgIntOperator operator) {
return operator.op(5, 10);
}
Then call the method with a lambda as parameter:
public static void main(String[] args) {
TwoArgIntOperator addTwoInts = (a, b) -> a + b;
int result = method(addTwoInts);
System.out.println("Result: " + result);
}
Using your own interface has the advantage that you can have names that more clearly indicate the intent.
Answer from Joachim Sauer on Stack OverflowLambdas are purely a call-site construct: the recipient of the lambda does not need to know that a Lambda is involved, instead it accepts an Interface with the appropriate method.
In other words, you define or use a functional interface (i.e. an interface with a single method) that accepts and returns exactly what you want.
Since Java 8 there is a set of commonly-used interface types in java.util.function.
For this specific use case there's java.util.function.IntBinaryOperator with a single int applyAsInt(int left, int right) method, so you could write your method like this:
static int method(IntBinaryOperator op){
return op.applyAsInt(5, 10);
}
But you can just as well define your own interface and use it like this:
public interface TwoArgIntOperator {
public int op(int a, int b);
}
//elsewhere:
static int method(TwoArgIntOperator operator) {
return operator.op(5, 10);
}
Then call the method with a lambda as parameter:
public static void main(String[] args) {
TwoArgIntOperator addTwoInts = (a, b) -> a + b;
int result = method(addTwoInts);
System.out.println("Result: " + result);
}
Using your own interface has the advantage that you can have names that more clearly indicate the intent.
To use Lambda expression you need to either create your own functional interface or use Java functional interface for operation that require two integer and return as value. IntBinaryOperator
Using user defined functional interface
interface TwoArgInterface {
public int operation(int a, int b);
}
public class MyClass {
public static void main(String javalatte[]) {
// this is lambda expression
TwoArgInterface plusOperation = (a, b) -> a + b;
System.out.println("Sum of 10,34 : " + plusOperation.operation(10, 34));
}
}
Using Java functional interface
import java.util.function.IntBinaryOperator;
public class MyClass1 {
static void main(String javalatte[]) {
// this is lambda expression
IntBinaryOperator plusOperation = (a, b) -> a + b;
System.out.println("Sum of 10,34 : " + plusOperation.applyAsInt(10, 34));
}
}
There is no difference. It's up to you what the tradeoff is. But frankly, you're better off writing neither of these; instead, import static java.util.Comparator.comparingInt and do Arrays.sort(strArray, comparingInt(String::length)). The first version isn't exactly more type-safe; the second version will infer exactly the same type information and enforce it exactly as much.
The lambda function element type is decided by the operate element type.(strArray):
public static <T> void sort(T[] a, Comparator<? super T> c) {
}
the comparator type is generics and it's decided by your passed parameter type.
The other lambda function is also same:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
T reduce(T identity, BinaryOperator<T> accumulator);
...
so specify parameter type or not, the compiler already has known what's the parameter type should be.
This is currently possible to solve but only in a pretty hackie way, but let me first explain a few things:
When you write a lambda, the compiler inserts a dynamic invoke instruction pointing to the LambdaMetafactory and a private static synthetic method with the body of the lambda. The synthetic method and the method handle in the constant pool both contain the generic type (if the lambda uses the type or is explicit as in your examples).
Now at runtime the LambdaMetaFactory is called and a class is generated using ASM that implements the functional interface and the body of the method then calls the private static method with any arguments passed. It is then injected into the original class using Unsafe.defineAnonymousClass (see John Rose post) so it can access the private members etc.
Unfortunately the generated Class does not store the generic signatures (it could) so you can't use the usual reflection methods that allow you to get around erasure
For a normal Class you could inspect the bytecode using Class.getResource(ClassName + ".class") but for anonymous classes defined using Unsafe you are out of luck. However you can make the LambdaMetaFactory dump them out with the JVM argument:
java -Djdk.internal.lambda.dumpProxyClasses=/some/folder
By looking at the dumped class file (using javap -p -s -v), one can see that it does indeed call the static method. But the problem remains how to get the bytecode from within Java itself.
This unfortunately is where it gets hackie:
Using reflection we can call Class.getConstantPool and then access the MethodRefInfo to get the type descriptors. We can then use ASM to parse this and return the argument types. Putting it all together:
Method getConstantPool = Class.class.getDeclaredMethod("getConstantPool");
getConstantPool.setAccessible(true);
ConstantPool constantPool = (ConstantPool) getConstantPool.invoke(lambda.getClass());
String[] methodRefInfo = constantPool.getMemberRefInfoAt(constantPool.size() - 2);
int argumentIndex = 0;
String argumentType = jdk.internal.org.objectweb.asm.Type.getArgumentTypes(methodRef[2])[argumentIndex].getClassName();
Class<?> type = (Class<?>) Class.forName(argumentType);
UPDATED with Jonathan's suggestion
Now ideally the classes generated by LambdaMetaFactory should store the generic type signatures (I might see if I can submit a patch to the OpenJDK) but currently this is the best we can do. The code above has the following problems:
- It uses undocumented methods and classes
- It is extremely vulnerable to code changes in the JDK
- It doesn't preserve the generic types, so if you pass List<String> into a lambda it will come out as List
Use a TypeRef to wrap the lambda expression.
@FunctionalInterface
interface TypeRef<T> {
T create();
default Class<?> getGenericType() {
return create().getClass();
}
}
public class Main {
public static void main(String[] args) {
System.out.println(getFactoryTypeParameter(() -> "hello"));
}
private static <T> Class<?> getFactoryTypeParameter(TypeRef<T> typeRef) {
return typeRef.getGenericType();
}
}
output:
class java.lang.String