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错误。

  • 相关阅读:
    接收HttpPost请求路由可以不用带去掉参数
    吟唱一首
    C# XML反序列化解析成对象集合
    两个对象中相同属性赋值
    CefSharp 笔记-1
    C# 笔记--Label设置背景图片
    C# 笔记--VS 2017包下载超时,连接失败
    SQLite——C#查询表时 该字符串未被识别为有效的 DateTime 错误
    C# 笔记--Sendkeys
    C# 笔记——MeasureString准确测量
  • 原文地址:https://www.cnblogs.com/haiq/p/case_of_java_generics.html
Copyright © 2011-2022 走看看