有以下的代码:

      try {
ArrayList<String> lstA = new ArrayList<String>();
ArrayList<Integer> lstB = new ArrayList<Integer>(); // ArrayList<Object> c = (ArrayList<Object>)lstA; Object d = lstA;
ArrayList<Object> e = (ArrayList<Object>)d;
System.out.println("e.toString=" + e.toString()); List<String> f = lstA;
System.out.println("f.toString=" + f.toString()); if (lstA.getClass() == lstB.getClass()) {
System.out.println("lstA.class == lstB.class");
}else{
System.out.println("lstA.class != lstB.class");
} String[] g = {"","2"};
Object[] h = (Object[])g;
System.out.println("h.toString=" + h.toString());
} catch (Exception ex) {
System.out.println("cast error!--" + ex.getMessage());
}

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

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

先看看被注释掉的第 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

再看看另外的一个例子:

     public static void main(String[] args) {
try {
testCaster1(SQLException.class);
} catch (SQLException e) {
System.out.println(String.format("SQLException=[%s]--%s", e.getClass().getName(), e.getMessage()));
} catch (Exception e) {
System.out.println(String.format("Exception=[%s]--%s", e.getClass().getName(), e.getMessage()));
} try {
testCaster2(SQLException.class);
} catch (SQLException e) {
System.out.println(String.format("SQLException=[%s]--%s", e.getClass().getName(), e.getMessage()));
} catch (Exception e) {
System.out.println(String.format("Exception=[%s]--%s", e.getClass().getName(), e.getMessage()));
}
} private static <E extends Throwable> void testCaster1(Class<E> clazz) throws E{
try {
throw new SQLException("测试抛出的SQL错误。");
} catch (Throwable e) {
throw (E)e;
}
}
private static <E extends Throwable> void testCaster2(Class<E> clazz) throws E{
try {
throw new IOException("测试抛出的IO错误。");
} catch (Throwable e) {
throw (E)e;
}
}

两个泛型方法 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错误。

关于 Java 泛型的一些有趣的例子的更多相关文章

  1. java 泛型的嵌套(map例子)

    package july7; //泛型加Map的输出! import java.util.Iterator; import java.util.Map; import java.util.Map.En ...

  2. 关于Java泛型实现原理的思考与一般用法示例总结

    面向对象的一个重要目标是对代码重用的支持.支持这个目标的一个重要机制就是泛型机制.在1.5版本之前,java并没有直接支持泛型实现,泛型编程的实现时通过使用继承的一些基本概念来完成的. 这种方式的局限 ...

  3. 浅析Java 泛型

    泛型是JavaSE5引入的一个新概念,但是这个概念在编程语言中却是很普遍的一个概念.下面,根据以下内容,我们总结下在Java中使用泛型. 泛型使用的意义 什么是泛型 泛型类 泛型方法 泛型接口 泛型擦 ...

  4. java泛型基础

    泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 这种参数类型可以用在类.接口和方法的创建中, 分别称为泛型类.泛型接口.泛型方法.  Ja ...

  5. 初识java泛型

    1 协变数组类型(covariant array type) 数组的协变性: if A IS-A B then A[] IS-A B[] 也就是说,java中的数组兼容,一个类型的数组兼容他的子类类型 ...

  6. java泛型上下限

    前言: java的泛型上下限不是很好理解,尤其像我这种菜鸡.反反复复看了好几遍了...,真是... 一.简单的继承体系 class Person{} class Student extends Per ...

  7. 【Java心得总结四】Java泛型下——万恶的擦除

    一.万恶的擦除 我在自己总结的[Java心得总结三]Java泛型上——初识泛型这篇博文中提到了Java中对泛型擦除的问题,考虑下面代码: import java.util.*; public clas ...

  8. 【Java心得总结三】Java泛型上——初识泛型

    一.函数参数与泛型比较 泛型(generics),从字面的意思理解就是泛化的类型,即参数化类型.泛型的作用是什么,这里与函数参数做一个比较: 无参数的函数: public int[] newIntAr ...

  9. 初识Java泛型以及桥接方法

    泛型的由来 在编写程序时,可能会有这样的需求:容器类,比如java中常见的list等.为了使容器可以保存多种类型的数据,需要编写多种容器类,每一个容器类中规定好了可以操作的数据类型.此时可能会有Int ...

随机推荐

  1. opencv3.2将中文输出到图片上

    opencv自带的putText函数无法输出utf8类型的字符,因此无法将中文打印到图片上.用这篇文章的freetype可以实现中文输出,但是需要将字符解码转码比较麻烦,而Pillow的Image函数 ...

  2. Phpcms V9手机门户设置教程:怎么用PC V9做手机网站

    一.在PHPcms V9管理后台设置手机门户 1.1.开启手机网站.位置:模块 >手机门户 > 添加手机站点,具体设置可参照截图: 填写站点名和LOGO文件相对位置,绑定用于手机网站的二级 ...

  3. 2014-07-08 hibernate tenancy

    http://en.wikipedia.org/wiki/Multitenancy http://www.infoq.com/news/2012/01/hibernate-4-released htt ...

  4. 5 -- Hibernate的基本用法 --4 4 数据库方言

    Hibernate底层依然使用SQL语句来执行数据库操作,虽然所有关系数据库都支持使用标准SQL语句,但所有数据库都对标准SQL进行了一些扩展,所以在语法细节上存在一些差异.因此,Hibernate需 ...

  5. iOS Ad hoc

    There's one situation in which you need an Ad Hoc profile, and that's when you want to test Push Not ...

  6. 【Java并发编程三】闭锁

    1.什么是闭锁? 闭锁(latch)是一种Synchronizer(Synchronizer:是一个对象,它根据本身的状态调节线程的控制流.常见类型的Synchronizer包括信号量.关卡和闭锁). ...

  7. Entity Framework6测试使用

    Entity Framework6安装完成后测试下 上一篇中完成了对Entity Framework6的下载安装,一下做一个简单的数据添加测试 1.创建一个简单的控制台测试项目 2.创建实体数据模型 ...

  8. jQuery事件处理(六)

    1.通过一步步调试的的方法观察了一下存放到cache中的事件及其处理程序的数据格式: { events : { // 根据事件类型存放添加到该元素上的所有事件,下面以click为例 click : [ ...

  9. Android 冷启动时间优化

    一 下载工具: 1.MaterialColdStart https://github.com/DreaminginCodeZH/MaterialColdStart 2.AndroidSVGScript ...

  10. c实现的list

    // clist.cpp : 定义控制台应用程序的入口点.// #include "stdafx.h"#include <stdio.h>#include <ma ...