Java泛型

  所谓泛型,就是变量类型的参数化。泛型是java1.5中引入的一个重要特征,通过引入泛型,可以使编译时类型安全,运行时更少抛出ClassCastException的可能。一提到参数化,最熟悉的就是定义方法是由形参,然后调用此方法时传递实参。那么参数化该怎么理解,顾名思义,就是将类型由原来的具体类型参数化,类似于方法中的变量参数,此是类型也可定义成参数形式,然后在使用,调用时传入具体类型。使用泛型是如果不提供参数类型,即泛型类没有参数化,系统会警告,此时类型为Object。

为什么使用泛型

  首先看一段代码:

 List list=new ArrayList();
list.add("asd");
list.add("qwe");
list.add(123);
for(int i=0;i<list.size();i++){
String name=(String) list.get(i);//1
System.out.println("name: "+name);
}

  在循环当中,由于忘记了之前的list中加入了Integer类型的值或者是其他原因,很容易出现类似于//1中的错误,因为在编译阶段正常,但是在运行时会出现ClassCastException异常。当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,该对象的编译类型变成了Object类型,但其运行时类型仍然为其本身类型,因此//1处取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现转化异常。

  泛型可以限制加入集合当中的元素类型,是编译时不出现问题,运行时就不会抛出ClassCastException异常。使用泛型的典型例子,是在集合中的泛型使用。

  在使用泛型前,存入集合中的元素可以是任何类型的,当从集合中取出时,所有的元素都是Object类型,需要进行向下的强制类型转换,转换到特定的类型

  比如:

List myIntList = new LinkedList(); // 1

myIntList.add(new Integer(0)); // 2

Integer x = (Integer) myIntList.iterator().next(); // 3   

  第三行的这个强制类型转换可能会引起运行时的错误。

  泛型的思想就是由程序员指定类型,这样集合就只能容纳该类型的元素。

  使用泛型:

List<Integer> myIntList = new LinkedList<Integer>(); // 1'

myIntList.add(new Integer(0)); // 2'

Integer x = myIntList.iterator().next(); // 3'

  将第三行的强制类型转换变为了第一行的List类型说明,编译器会为我们检查类型的正确性。这样,代码的可读性和健壮性也会增强。

自定义泛型类,泛型接口,泛型方法

  先简单定义一个类: 

 1 public class GenericTest {
2
3 public static void main(String[] args) {
4
5 Box<String> name = new Box<String>("corn");
6 System.out.println("name:" + name.getData());
7 }
8
9 }
10
11 class Box<T> {
12
13 private T data;
14
15 public Box() {
16
17 }
18
19 public Box(T data) {
20 this.data = data;
21 }
22
23 public T getData() {
24 return data;
25 }
26
27 } 

在泛型接口、泛型类和泛型方法的定义过程中,我们常见的如T、E、K、V等形式的参数常用于表示泛型形参,由于接收来自外部使用时候传入的类型实参。那么对于不同传入的类型实参,生成的相应对象实例的类型是不是一样的呢?

 1 public class GenericTest {
2
3 public static void main(String[] args) {
4
5 Box<String> name = new Box<String>("corn");
6 Box<Integer> age = new Box<Integer>(712);
7
8 System.out.println("name class:" + name.getClass()); // com.qqyumidi.Box
9 System.out.println("age class:" + age.getClass()); // com.qqyumidi.Box
10 System.out.println(name.getClass() == age.getClass()); // true
11
12 }
13
14 }

由此,我们发现,在使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,传入不同泛型实参的泛型类在内存上只有一个,即还是原来的最基本的类型(本实例中为Box),当然,在逻辑上我们可以理解成多个不同的泛型类型。

究其原因,在于Java中的泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。

对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。

泛型和子类:

List<String> ls = new ArrayList<String>(); // 1

List<Object> lo = ls; // 2

一个String类型的List是一个Object类的List吗?

不可以,Java编译器将会在第二行产生一个编译错误,因为它们的类型不匹配。

这样就避免了如果lo引入加入Object类型的对象,而ls引用试图将其转换为String类型而引发错误。所以编译器阻止了这种可能。

继承泛型类别:

父类:

public class Parent<T1,T2>
{
private T1 foo1;
private T2 foo2; public T1 getFoo1()
{
return foo1;
}
public void setFoo1(T1 foo1)
{
this.foo1 = foo1;
}
public T2 getFoo2()
{
return foo2;
}
public void setFoo2(T2 foo2)
{
this.foo2 = foo2;
} }

子类继承父类:

public class Child<T1, T2, T3> extends Parent<T1, T2>
{
private T3 foo3; public T3 getFoo3()
{
return foo3;
} public void setFoo3(T3 foo3)
{
this.foo3 = foo3;
} }
 

实现泛型接口:

泛型接口:

public interface ParentInterface<T1,T2>
{
public void setFoo1(T1 foo1);
public void setFoo2(T2 foo2);
public T1 getFoo1();
public T2 getFoo2(); }

  子类实现泛型接口:

public class ChildClass<T1,T2> implements ParentInterface<T1, T2>
{
private T1 foo1;
private T2 foo2; @Override
public void setFoo1(T1 foo1)
{
this.foo1 = foo1; }
@Override
public void setFoo2(T2 foo2)
{
this.foo2 = foo2;
}
@Override
public T1 getFoo1()
{
return this.foo1;
}
@Override
public T2 getFoo2()
{
return this.foo2;
} }

调用泛型方法语法格式如下:

定义泛型方法时,必须在返回值前边加一个<T>,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。

Class<T>的作用就是指明泛型的具体类型,而Class<T>类型的变量c,可以用来创建泛型类的对象。

为什么要用变量c来创建对象呢?既然是泛型方法,就代表着我们不知道具体的类型是什么,也不知道构造方法如何,因此没有办法去new一个对象,但可以利用变量c的newInstance方法去创建对象,也就是利用反射创建对象。

泛型方法要求的参数是Class<T>类型,而Class.forName()方法的返回值也是Class<T>,因此可以用Class.forName()作为参数。其中,forName()方法中的参数是何种类型,返回的Class<T>就是何种类型。在本例中,forName()方法中传入的是User类的完整路径,因此返回的是Class<User>类型的对象,因此调用泛型方法时,变量c的类型就是Class<User>,因此泛型方法中的泛型T就被指明为User,因此变量obj的类型为User。

当然,泛型方法不是仅仅可以有一个参数Class<T>,可以根据需要添加其他参数。

为什么要使用泛型方法呢?因为泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活。

泛型的继承规则:

设计两个类,它们之间有继承关系:
public class B {
    public void fun(){
        System.out.println("A.fun()");
    }
}

public class BSub extends B{
    @Override
    public void fun(){
        System.out.println("BSub.fun()");
    }

public static void main(String[] args) {
        Pair<BSub> p1 = new Pair<BSub>();
       
        Pair p2 = p1;
        p2.setFirst(new Date());
        BSub b = p1.getFirst();
        b.fun();
    }
}

但其实Pair<B>和Pair<BSub>之间没有什么联系,绝对没有继承的关系。这种限制的原因是:
Pair<BSub> bsp = new Pair<BSub>();
Pair<B> bp = bsp;//不合法,错误:类型不兼容,但现在我们假设可以这样做
bp.setFirst(new B());
最后一句合法,但是bp与bsp是引用的同一个对象,那么可以将B对象装入Pair<BSub>中看起来也是不和情理的,退一步假设即使可以这样做,也会引发类型转换的错误,因为B是超类不能转换成子类。所以不如禁止这样做。
但是下面的代码是合法的:

Pair<BSub> p1 = new Pair<BSub>();
//        Pair<B> p = p1;//不合法,类型不兼容

Pair p2 = p1;
        p2.setFirst(new Date());
        BSub b = p1.getFirst();
        b.fun();
       
        Pair<B> p3 = new Pair<B>();
//        Pair<BSub> p4 = p3;//不合法,同样类型不兼容

但是会产生ClassCastException,把“谎话”说圆还是有难度的,这是Java对泛型实现的一个代价吧。
最后,泛型类可以拓展或实现其它的泛型类。就这一点而言,与普通的类没有什么区别。例如,ArrayList<T>类实现List<T>接口,这意味着一个ArrayList<BSub>可以被转换为一个List<BSub>。但是,正像前面所讨论的,一个ArrayList<BSub>不是一个ArrayList<B>或者List<B>。
下图显示了它们之间的关系:

参考资料:

http://www.cnblogs.com/mengdd/archive/2013/01/21/2869778.html

http://www.cnblogs.com/lwbqqyumidi/p/3837629.html

http://www.cnblogs.com/iyangyuan/archive/2013/04/09/3011274.html

http://www.cnblogs.com/sunwei2012/archive/2010/10/08/1845938.html

http://www.cnblogs.com/Fskjb/archive/2009/08/23/1552506.html

http://www.cnblogs.com/yinhaiming/articles/1749738.html

http://www.cnblogs.com/nerxious/archive/2012/12/21/2828121.html

http://www.cnblogs.com/anrainie/archive/2012/03/09/2387177.html

http://blog.sina.com.cn/s/blog_44c1e6da0100cus8.html

Java泛型学习一的更多相关文章

  1. Java泛型学习笔记 - (七)浅析泛型中通配符的使用

    一.基本概念:在学习Java泛型的过程中, 通配符是较难理解的一部分. 主要有以下三类:1. 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List< ...

  2. Java泛型学习笔记--Java泛型和C#泛型比较学习(一)

    总结Java的泛型前,先简单的介绍下C#的泛型,通过对比,比较学习Java泛型的目的和设计意图.C#泛型是C#语言2.0和通用语言运行时(CLR)同时支持的一个特性(这一点是导致C#泛型和Java泛型 ...

  3. java泛型学习(2)

    一:深入泛型使用.主要是父类和子类存在泛型的demo /** * 父类为泛型类 * @author 尚晓飞 * @date 2014-7-15 下午7:31:25 * * * 父类和子类的泛型. * ...

  4. Java泛型学习---第二篇

    泛型学习第一篇 1.泛型之擦拭法 泛型是一种类似"模板代码"的技术,不同语言的泛型实现方式不一定相同. Java语言的泛型实现方式是擦拭法(Type Erasure). 所谓擦拭法 ...

  5. java泛型学习(1)

    java泛型(Generices Type) --->概念:泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和 ...

  6. Java 泛型学习总结

    前言 Java 5 添加了泛型,提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质是参数化类型,可以为以前处理通用对象的类和方法,指定具体的对象类型.听起来有点抽象, ...

  7. java 泛型学习随笔

    对于java 泛型 编译时处理,运行时擦除的特点理解 对于编译时处理 在使用泛型相关的类或方法时,如果声明时的类型和具体使用时的类型不一致则直接会编译不通过 对于运行时擦除 当在运行时对两个相同类型但 ...

  8. Java泛型学习笔记 - (六)泛型的继承

    在学习继承的时候, 我们已经知道可以将一个子类的对象赋值给其父类的对象, 也就是父类引用指向子类对象, 如: Object obj = new Integer(10); 这其实就是面向对象编程中的is ...

  9. java 泛型学习

    http://blog.csdn.net/archie2010/article/details/6232228 学习集合框架的时候经常用hasmap<Integer,Integer>就是泛 ...

随机推荐

  1. Django基础四之模板系统

    一 语法   模板渲染的官方文档 关于模板渲染你只需要记两种特殊符号(语法): {{  }}和 {% %} 变量相关的用{{}},逻辑相关的用{%%}. 二 变量 在Django的模板语言中按此语法使 ...

  2. drupal7 查看哪些模块实现了某个钩子

    module_implements($hook) 可参考函数module_invoke_all function module_invoke_all($hook) { $args = func_get ...

  3. vmware下载存储vmdk文件后缀变-flat处理方式

    将vmware存储中的虚拟机vmdk文件下载到本地,下载完成后,下载了2个vmdk文件 一份为:xx.vmdk (通常1KB左右)  一份为:xx-flat.vmdk (此为源文件正常大小)     ...

  4. VMware桥接模式下主机和和虚机间互相ping不通的处理方法

    在 "编辑"->"虚拟网络编辑器" 里面的vmnet0 桥接模式 里面是自动连接,把他改为真实的物理网卡即可,如下图:

  5. OpenGL学习--05--纹理立方体--BMP文件格式详解(转载)

    http://blog.csdn.net/o_sun_o/article/details/8351037 BMP文件格式详解 BMP文件格式详解(BMP file format) BMP文件格式,又称 ...

  6. 用华为eNSP模拟器配置Hybrid、Trunk和Access三种链路类型端口

    上一篇文章写到三层交换机实现多个VLAN之间互相通讯,有朋友提问要如何进行配置,可有案例分析.其实那天我在写的时候也有做过模拟,只是后来没有保存.今天重新模拟一次,并附上详细配置命令,希望能够帮助到大 ...

  7. 转:C# WinForm窗体及其控件的自适应

    一.说明 2012-11-30 曾经写过 <C# WinForm窗体及其控件自适应各种屏幕分辨率>  ,其中也讲解了控件自适应的原理.近期有网友说,装在panel里面的控件,没有效果? 这 ...

  8. Lambda表达式学习记录

    Lambda表达式可以简化C#编程的某些方面,用法非常灵活.因此也不容易掌握. 下边是我学Lambda表达式的一点记录. 1.Lambda表达式是与委托紧密联系的.只要有委托参数类型的地方,就可以使用 ...

  9. 使用Charles进行网络请求抓包解析

    使用Charles进行网络请求抓包解析 0. 懒人的福音(⌐■_■)(破解版下载地址,记得安装java库支持) http://pan.baidu.com/s/1c08ksMW 1. 查看电脑的ip地址 ...

  10. 将jsonModel转化为文件

    将jsonModel转化为文件 这个类是我自己写着用的,用于将字典文件直接转换成Model的文件,省去你写无数Model中属性的代码: TransformDictionary.h 与 Transfor ...