3

When the code below is run:

import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;

class A {
    private static final List<Adapter<? extends Number>> adapters = Arrays.asList(
            Integer::valueOf,
            s -> Long.valueOf(s),
            (Adapter<Float>) s -> Float.valueOf(s),
            new Adapter<Double>() {
                @Override
                public Double adapt(String s) {
                    return Double.valueOf(s);
                }
            }
    );

    private interface Adapter<T> {
        T adapt(String s);

        default Type type() {
            try {
                return getClass().getMethod("adapt", String.class).getReturnType();
            } catch (NoSuchMethodException e) {
                throw new AssertionError(e);
            }
        }
    }

    public static void main(String[] args) {
        for (Adapter<?> adapter : adapters) {
            System.out.printf("Got adapter for type %s%n",
                    adapter.type().getTypeName());
        }
    }
}

I get:

% javac A.java
% java -version
java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
% java A
Got adapter for type java.lang.Object
Got adapter for type java.lang.Object
Got adapter for type java.lang.Object
Got adapter for type java.lang.Double

I am curious why only the last adapter is recognized as Double, and the rest are Object (as opposed to Integer, Long, and Float). At the very least, I would have expected them to be for Number.

Kranu
  • 2,557
  • 16
  • 22

1 Answers1

4
        new Adapter<Double>() {
            @Override
            public Double adapt(String s) {
                return Double.valueOf(s);
            }
        }

For the above code, it will create an implementation class for Adapter interface, after you compiled you can see: A$1.class is under your directory, this is an imeplementated class for new Adapter. and you can use javap -c A\$1 to check the bytecode, like:

Compiled from "A.java"
final class A$1 implements A$Adapter<java.lang.Double> {
  A$1();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public java.lang.Double adapt(java.lang.String);
    Code:
       0: aload_1
       1: invokestatic  #2                  // Method java/lang/Double.valueOf:(Ljava/lang/String;)Ljava/lang/Double;
       4: areturn

  public java.lang.Object adapt(java.lang.String);
    Code:
       0: aload_0
       1: aload_1
       2: invokevirtual #3                  // Method adapt:(Ljava/lang/String;)Ljava/lang/Double;
       5: areturn
}

as you can see the A$1.adapt class has inferred the Double type by compiler.

and for Lambda expression, the compiler just deal it as invokedynamic #2, 0 // InvokeDynamic #0:adapt:()LA$Adapter;, so caused by type erasure, this will be Object type for this.

chengpohi
  • 14,064
  • 1
  • 24
  • 42