benf.org :  other :  cfr :  Java 8 Lambdas - Mystery 'getClass' with instance method refs.

Consider the following code:

    public void test(Stream<String> in) {
        Set<String> s = SetFactory.newSet();
        Stream<Boolean> numbers = in.map(s::contains);
    }

Compiled with java 8 B103

public void test(java.util.stream.Stream);
    Code:
       0: invokestatic  #2                  // Method org/benf/cfr/tests/support/SetFactory.newSet:()Ljava/util/Set;
       3: astore_2      
       4: aload_1       
       5: aload_2       
       6: invokedynamic #3,  0              // InvokeDynamic #0:apply:(Ljava/util/Set;)Ljava/util/function/Function;
      11: invokeinterface #4,  2            // InterfaceMethod java/util/stream/Stream.map:(Ljava/util/function/Function;)Ljava/util/stream/Stream;
      16: astore_3      
      17: return  

Compiled with java 8 B132

public void test(java.util.stream.Stream);
    Code:
       0: invokestatic  #2                  // Method org/benf/cfr/tests/support/SetFactory.newSet:()Ljava/util/Set;
       3: astore_2      
       4: aload_1       
       5: aload_2       
       6: dup           
       7: invokevirtual #3                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      10: pop           
      11: invokedynamic #4,  0              // InvokeDynamic #0:apply:(Ljava/util/Set;)Ljava/util/function/Function;
      16: invokeinterface #5,  2            // InterfaceMethod java/util/stream/Stream.map:(Ljava/util/function/Function;)Ljava/util/stream/Stream;
      21: astore_3      
      22: return  

... interesting - B132 adds an explicit call to getClass on the Set s.

I haven't yet found any 'official' description of a reason for this - it seems (internet based wisdom) that it's a null check, though I imagine that it's there to force any issues with classLoading for s to occur in the explicit original method, as opposed to inside the invokedynamic's builder, thus causing users to get slightly more understandable stack traces in the case of problems.

CFR 0_75 will generate the following.

    public void test(Stream<String> in) {
        Set s;
        Set set = s = SetFactory.newSet();
        set.getClass();
        Stream<Boolean> numbers = in.map(set::contains);
    }

.... but this is fixed in 0_76

    public void test(Stream<String> in) {
        Set s = SetFactory.newSet();
        Stream<Boolean> numbers = in.map(s::contains);
    }

Last updated 03/2014