JAVA 的重载是运行时决定还是编译的时候决定?正确使用泛型

xiaoxiao2021-02-28  94

首先看个例子

第一个片段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()); }
另外判断类型是否是某个接口的子类实现或者本身的正确姿势
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 <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
转载请注明原文地址: https://www.6miu.com/read-40356.html

最新回复(0)