MethodHandles.lookup().lookupClass() vs getClass()

Viewed 659

Can anyone tell me the (subtle) differences of

version 1:

protected final Logger log = Logger.getLogger(getClass());

vs

version 2:

protected final Logger log = Logger.getLogger(MethodHandles.lookup().lookupClass());

Is version 2 in general faster than version 1?

I guess version 1 uses reflection (on runtime) to determine the current class while version 2 does not need to use reflection, or (is the check done on build time)?

2 Answers

There is no reflection involved in you first case. Object#getClass() is mapped to JVM's native method.

Your second case is not drop-in replacement for Object#getClass(), it is used to lookup method handles.

So subtle difference is, they are used for completely different purposes.

These are entirely different things. The documentation of lookupClass, specifically says:

Tells which class is performing the lookup. It is this class against which checks are performed for visibility and access permissions

So it's the class which performs the lookup. It's not necessarily the class where you call MethodHandles.lookup(). What I mean by that is that this:

Class<?> c = MethodHandles.privateLookupIn(String.class, MethodHandles.lookup()).lookupClass();
System.out.println(c); 

will print String.class and not the class where you define this code.

The only "advantage" (besides confusing every reader of this code), is that if you copy/paste that log creation line across various source files, it will use the proper class, if you, by accident, don't edit it (which probably happens).

Also notice that:

protected final Logger log = Logger.getLogger(getClass());

should be a static field, usually, and you can't call getClass if it is.

A JMH test shows that there is no performance gain to obfuscate your code that much:

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
public class LookupTest {

    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder().include(LookupTest.class.getSimpleName())
                .verbosity(VerboseMode.EXTRA)
                .build();
        new Runner(opt).run();
    }
    
    @Benchmark
    @Fork(3)
    public Class<?> getClassCall() {
        return getClass();
    }
    
    @Benchmark
    @Fork(3)
    public Class<?> methodHandlesInPlaceCall() {
        return MethodHandles.lookup().lookupClass();
    }
    
    @Benchmark
    @Fork(3)
    public Class<?> methodHandlesCall() {
        return LOOKUP.lookupClass();
    }
}

results:

Benchmark                            Mode  Cnt  Score   Error  Units
LookupTest.getClassCall              avgt   15  2.264 ± 0.044  ns/op
LookupTest.methodHandlesCall         avgt   15  2.262 ± 0.030  ns/op
LookupTest.methodHandlesInPlaceCall  avgt   15  4.890 ± 0.783  ns/op
Related