第一个片段A的部分 传入的实际类型是String希望调用C片段,但是实际上是调用的B。
敲黑板:Java的泛型是运行时就擦除了的。 不要出现参数数量一样的方法重载,可能出错不说,而且完全不清晰。 T 会擦除成Object。 调哪个编译时就确定了。
我们看下Optional的泛型如何可以准确找到isEmpty(String s)
Optional<String> str = Optional.of("me"); str.ifPresent(v->{ boolean exit = Util.isEmpty(v); });一个是去掉泛型,避免同一后再细化。第二种是修改重载的部分如下:
public static <T> void ifNotEmpty(T t, Consumer<? super T> consumer) { if (!isEmpty(t)) { consumer.accept(t); } } public static boolean isEmpty(Object obj) { if (obj == null) return true; Class<?> clazz = obj.getClass(); if (clazz.isArray()) { if (clazz == byte[].class) // eClass.getComponentType() == byte.class return ((byte[]) obj).length == 0; if (clazz == short[].class) return ((short[]) obj).length == 0; if (clazz == int[].class) return ((int[]) obj).length == 0; if (clazz == long[].class) return ((long[]) obj).length == 0; if (clazz == char[].class) return ((char[]) obj).length == 0; if (clazz == float[].class) return ((float[]) obj).length == 0; if (clazz == double[].class) return ((double[]) obj).length == 0; if (clazz == boolean[].class) return ((boolean[]) obj).length == 0; Object[] objArr = (Object[]) obj; return objArr.length == 0; } if (String.class.isAssignableFrom(clazz)) { return ((String) obj).length() == 0; } if (Map.class.isAssignableFrom(clazz)) { return ((Map<?, ?>) obj).size() == 0; } if (Collection.class.isAssignableFrom(clazz)) { return ((Collection<?>) obj).size() == 0; } throw new SysException("unkown classType {}", clazz.getCanonicalName()); }下面两段代码是一样的
public static boolean isEmpty(Collection<?> t) { return null == t || 0 == t.size(); } public static <T extends Collection<?>> boolean isEmpty(T t) { return null == t || 0 == t.size(); }编译后:
public static boolean isEmpty(java.util.Collection<?>); Code: 0: aconst_null 1: aload_0 2: if_acmpeq 15 5: iconst_0 6: aload_0 7: invokeinterface #2, 1 // InterfaceMethod java/util/Collection.size:()I 12: if_icmpne 19 15: iconst_1 16: goto 20 19: iconst_0 20: ireturn public static void main(java.lang.String[]); Code: 0: new #3 // class java/util/ArrayList 3: dup 4: invokespecial #4 // Method java/util/ArrayList."<init>":()V 7: astore_1 8: aload_1 9: invokestatic #5 // Method isEmpty:(Ljava/util/Collection;)Z 12: pop 13: return第二种为
public static <T extends java.util.Collection<?>> boolean isEmpty(T); Code: 0: aconst_null 1: aload_0 2: if_acmpeq 15 5: iconst_0 6: aload_0 7: invokeinterface #2, 1 // InterfaceMethod java/util/Collection.size:()I 12: if_icmpne 19 15: iconst_1 16: goto 20 19: iconst_0 20: ireturn public static void main(java.lang.String[]); Code: 0: new #3 // class java/util/ArrayList 3: dup 4: invokespecial #4 // Method java/util/ArrayList."<init>":()V 7: astore_1 8: aload_1 9: invokestatic #5 // Method isEmpty:(Ljava/util/Collection;)Z 12: pop 13: return可以看到main方法中在编译后已经指定具体方法了 如果我们将main函数的代码修改如下
public static void main(String args[]){ List<Object> list = new ArrayList<>(); Object o = list; isEmpty(o); }反编译会发现调用的是isEmpty(Object o)而不是isEmpty(Collection list),即不是根据实际类型来寻找具体的重载方法,而是在编译的时候就已经决定了
public static void main(java.lang.String[]); Code: 0: new #3 // class java/util/ArrayList 3: dup 4: invokespecial #4 // Method java/util/ArrayList."<init>":()V 7: astore_1 8: aload_1 9: astore_2 10: aload_2 11: invokestatic #5 // Method isEmpty:(Ljava/lang/Object;)Z 14: pop 15: return