第一个片段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 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());
}
System.out.println(Map.class.isAssignableFrom(HashMap.class));
System.out.println(null instanceof String);
System.out.println(String.class.isInstance("a"));
下面两段代码是一样的
public static boolean isEmpty(Collection> t) {
return null == t || 0 == t.size();
}
public static > 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."":()V
7: astore_1
8: aload_1
9: invokestatic #5 // Method isEmpty:(Ljava/util/Collection;)Z
12: pop
13: return
第二种为
public static > 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."":()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
反编译会发现调用的是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."":()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