zoukankan      html  css  js  c++  java
  • 关于 Java 泛型的一些有趣的例子

    有以下的代码:

     1      try {
     2             ArrayList<String> lstA = new ArrayList<String>();
     3             ArrayList<Integer> lstB = new ArrayList<Integer>();
     4             
     5 //            ArrayList<Object> c = (ArrayList<Object>)lstA;
     6             
     7             Object d = lstA;
     8             ArrayList<Object> e = (ArrayList<Object>)d;
     9             System.out.println("e.toString=" + e.toString());
    10             
    11             List<String> f = lstA;
    12             System.out.println("f.toString=" + f.toString());
    13             
    14             if (lstA.getClass() == lstB.getClass()) {
    15                 System.out.println("lstA.class == lstB.class");
    16             }else{
    17                 System.out.println("lstA.class != lstB.class");
    18             }
    19             
    20             String[] g = {"","2"};
    21             Object[] h = (Object[])g;
    22             System.out.println("h.toString=" + h.toString());
    23         } catch (Exception ex) {
    24             System.out.println("cast error!--" + ex.getMessage());
    25         }

    运行以上代码,估计会输出什么?会产生异常吗?

    实际上,这些代码都能正常运行。

    先看看被注释掉的第 5 行:

    ArrayList<Object> c = (ArrayList<Object>)lstA;

    这行会产生编译错误,所以被注释掉了。可是换一种方式却可以通过编译器的检查,也就是接下来的7-9行:

    Object d = lstA;
    ArrayList<Object> e = (ArrayList<Object>)d;
    System.out.println("e.toString=" + e.toString());

    这几行代码不光是骗过编译器,也能够正常运行。

    继续看接下来的 11-12 行:

    List<String> f = lstA;
    System.out.println("f.toString=" + f.toString());

    与前面 7-9 行的有所不同的是,前面是对“类型参数”做了转换,而此处是类型本身变了,即将 ArrayList 转为其超类接口 List ,这能行吗?

    运行便知!OK,执行通过!

    继续看看接下来的 14-18 行:

    if (lstA.getClass() == lstB.getClass()) {
          System.out.println("lstA.class == lstB.class");
    }else{
          System.out.println("lstA.class != lstB.class");
    }

    猜猜这里输出的结果是什么?

    答案是:

    "lstA.class == lstB.class"

    为什么呢?难道 lstA 和 listB 是同一个类型,可是他们明明一个是 ArrayList<String>,一个是ArrayList<Integer> 。

    原来这就是泛型的障眼法。

    与 C# 的泛型不同,java 的泛型实际上更像是个语法上的东西,在运行时是没有“泛型”的存在的,运行时 lstA 和 lstB 的类型都是 ArrayList (或者说等同 ArrayList<Object>)。

    我们再看看最后的 20-22 行:

    String[] g = {"","2"};
    Object[] h = (Object[])g;
    System.out.println("h.toString=" + h.toString());

    这和前面的 11 行类似,对于数组同样也行得通。当然这跟泛型没什么关系,这种称为“协变”,只是形式上与一些泛型的操作类似,所以放在一起比照。

    以上全部代码在我本机运行的输出结果如下:

    e.toString=[]
    f.toString=[]
    lstA.class == lstB.class
    h.toString=[Ljava.lang.String;@e09713

    再看看另外的一个例子:

     1     public static void main(String[] args) {
     2         try {
     3             testCaster1(SQLException.class);
     4         } catch (SQLException e) {
     5             System.out.println(String.format("SQLException=[%s]--%s", e.getClass().getName(), e.getMessage()));
     6         } catch (Exception e) {
     7             System.out.println(String.format("Exception=[%s]--%s", e.getClass().getName(), e.getMessage()));
     8         }
     9         
    10         try {
    11             testCaster2(SQLException.class);
    12         } catch (SQLException e) {
    13             System.out.println(String.format("SQLException=[%s]--%s", e.getClass().getName(), e.getMessage()));
    14         } catch (Exception e) {
    15             System.out.println(String.format("Exception=[%s]--%s", e.getClass().getName(), e.getMessage()));
    16         }
    17     }
    18     
    19     
    20     private static <E extends Throwable> void testCaster1(Class<E> clazz) throws E{
    21         try {
    22             throw new SQLException("测试抛出的SQL错误。");
    23         } catch (Throwable e) {
    24             throw (E)e;
    25         }
    26     }
    27     private static <E extends Throwable> void testCaster2(Class<E> clazz) throws E{
    28         try {
    29             throw new IOException("测试抛出的IO错误。");
    30         } catch (Throwable e) {
    31             throw (E)e;
    32         }
    33     }

    两个泛型方法 testCaster1 和 testCaster2 的逻辑是一样,用泛型 E 定义了 throws 抛出的异常类型,同时在内部捕捉 Throwable 并将其转换为声明的泛型参数返回。

    等等!!

    细心的你是不是发现了什么不对劲的地方?...

    是的,第 24 行和 31 行,明显异常类型 E 是泛型,调用者指定的具体类型是不确定的,而这两个方法一个抛出 SQLException,一个抛出 IOException,

    那接下来的转换要引发 ClassCastException 了吧?

    不过答案是否定的!它们运行得很好。

    看 main 方法中的 2-8 行,调用 testCaster1 抛出的 SQLException,最终被 catch(SQLException e) 捕捉到。

    再看接下来的 10-16 行,调用 testCaster2 抛出的 IOException,最终被 catch(Exception e) 捕捉到。

    在 testCaster2 的内部抛出的 IOException 似乎并没有按照调用者指定的泛型参数 SQLException 做强制转换,因为并没有 ClassCastException 发生。

    哈哈,这又是泛型的障眼法!

    全部输出结果如下:

    SQLException=[java.sql.SQLException]--测试抛出的SQL错误。
    Exception=[java.io.IOException]--测试抛出的IO错误。

  • 相关阅读:
    利用DTrace实时检测MySQl
    改进MySQL Order By Rand()的低效率
    RDS for MySQL查询缓存 (Query Cache) 的设置和使用
    RDS For MySQL 字符集相关说明
    RDS for MySQL 通过 mysqlbinlog 查看 binlog 乱码
    RDS for MySQL Mysqldump 常见问题和处理
    RDS for MySQL Online DDL 使用
    RDS MySQL 表上 Metadata lock 的产生和处理
    RDS for MySQL 如何使用 Percona Toolkit
    北京已成为投融资诈骗重灾区:存好骗子公司黑名单,谨防上当!
  • 原文地址:https://www.cnblogs.com/haiq/p/case_of_java_generics.html
Copyright © 2011-2022 走看看