Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java 8,Lambda Expressions working differently while same javassist is being used

I have a list of student, List<Student> and I am using javassist-3.9.0.GA jar.

when i am writing list.stream().collect(Collectos.grouping(Student::getCode)) it is giving Caused by: java.io.IOException: invalid constant type: 18 exception. When I change above statement as list.stream().collect(Collectos.grouping(p->p.getCode())) it works.

Byte code manupulation of p->p.getCode() and Student::getCode are not same?

how above two statement is working differently while same javassist is being used?

Summary: I have a class Like:

class Student{

   private String code;
   private String name;


   public String getCode(){
       return code;
   }
   public void setCode(){
       this.code=code;
   }

   public String getName(){
      return name;
   }
   public void setName(String name){
      this.name=name;
   }
}

List<Student> studentList=getStudentList();

Now below statement is working fine on javassist-3.9.0.GA jar

Map<String,List<TripDetail>> tripMap=studentList.stream().collect(Collectors.groupingBy(p -> p.getCode()));

but with the same Jar below statement is giving invalid constant type: 18 exception

Map<String,List<TripDetail>> tripMap=studentList.stream().collect(Collectors.groupingBy(Student::getCode));

So as per my knowledge p -> p.getCode() and Student::getCode both are same,so either both should give that exception or both should be working.

like image 731
Nishat Avatar asked Oct 30 '22 05:10

Nishat


1 Answers

According to an answer here, Javassist 3.9 doesn't fully support the Java 8 features you're using. Upgrade Javassist, if you can. If not, you're stuck with the p -> p.getCode() version.

As for the difference between the two syntaxes, p -> p.getCode() is a lambda expression that creates a new anonymous function invoking getCode() on its argument. The argument type comes from the functional interface the expression is fulfilling.

Meanwhile, Student::getCode doesn't create a new function, it's simply a lambda expression that references the existing getCode method of Student.

Fundamentally the difference is that p -> p.getCode() does not belong to anybody, whereas Student::getCode belongs to the Student.

I compiled a quick example to see what effect that has on the method invocation. With the p -> p.getCode() version, this bytecode is produced:

SourceFile: "LambdaExampleAnonymous.java"
BootstrapMethods:
  0: #51 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #52 (Ljava/lang/Object;)Ljava/lang/Object;
      #55 invokestatic example/LambdaExampleAnonymous.lambda$0:(Lexample/Student;)Ljava/lang/String;
      #56 (Lexample/Student;)Ljava/lang/String;

With the Student::getCode version, you get this:

SourceFile: "LambdaExampleReference.java"
BootstrapMethods:
  0: #44 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #45 (Ljava/lang/Object;)Ljava/lang/Object;
      #50 invokevirtual example/Student.getCode:()Ljava/lang/String;
      #52 (Lexample/Student;)Ljava/lang/String;

You can see that the anonymous lambda function is invoked with invokestatic, while the referenced method is invoked with invokevirtual.

like image 94
Sam Avatar answered Nov 15 '22 04:11

Sam