一、概述。
前段时间,有同事说想要了解一下Java中的泛型相关知识,想想自己对泛型也不是特别了解,只是简单的应用而已,作为一个有追求的工程师,怎么能够这个样子呢。正好借此机会,也了解了一下,并抽时间整理出来,也能使自己记得更牢一些。我最后会将后续文章中的代码实例上传到网上供大家下载,并附上下载地址
二、知识点。
1.概念。
泛型是JDK1.5中引入的,在代码编译期就会对代码进行检查,后会将泛型去掉(擦除),Class文件中是不包含泛型信息的,不进入运行阶段。常用的英文符合有T、E、K、V、N,其中T为Type,泛指所有Java类型中的某一种,也是我们最常见最常用的一种,;E为Element,常用在集合类中,集合中存放的都是元素;K为Key,键值对中的key,V为Value,键值对中的value;N为Number,常用作数值类型。另外我们也可以自定义,如S、U等。泛型个人理解就是通过泛型,我们可以将类型动态化,不在局限于一种,举个例子,我们在开发过程中定义一个方法:private void setPrice(int price){},方法参数中price的数据类型为int类型,它是不可变的,这时候如果我们添加一种需求就是这个产品的价格也可能是float类型的,按照我们通常的开发习惯估计80%的人就会重载一个setPrice(float price)的方法。但是有了泛型,这个问题就简单了,我们可以这样定义一个方法:public <T extends Number> void set(T price){}
这样我们就可以实现,一个方法又可以传int 又可以传float,看一下代码:
public <T extends Number> void set(T price){ System.out.println("Log_"+price); } Main m = new Main(); m.set(11); m.set(11.11);
输出:
Log_11 Log_11.11
我们通过泛型实现了在一个方法中传递不同数据类型的数据,编译器不报错。这是在方法中的应用。在类中其实也是一样的,看一下:
public class Person<T>{ private T a; public Person(T a){ this.a=a; } } A<String> a=new A<String>("aaa"); A<Integer> b=new A<Integer>(111);通过在类中使用泛型,我们可以为Person中的T指定不同的数据类型。这就是泛型的作用。泛型可以作用在接口上,如,Java源码中的集合父类public interface Collection<E> extends Iterable<E>,也可以作用在类和方法上,上边也已经说了。
2.与Object对比
可能你会说Object也能实现上边的功能,没错,但是Object是不安全的,而且需要进行强制转换。而泛型可以很好的解决上面的问题,例如,在List中如果我们不使用泛型,它默认接收的就是Object类型的,我们可以看一下:
List list=new ArrayList(); list.add("zhangsan"); list.add(111); System.out.println((String)list.get(0)); System.out.println((String)list.get(1));//编译时不抱错,运行时报错。
在我们添加数据时,添加了不同类型的数据,编译器不会报错,因为List不指定泛型类型时默认接收Object,但是如果在输出时并不知道,就会很容易出现ClassCastException错误,所以说他是不安全的,很容易出问题,而且再取数据时,必须进行强制转化。下边看一下泛型实现:
List<String> list=new ArrayList<String>(); list.add("zhangsan"); //list.add(111);//编译期就会报错; System.out.println(list.get(0)); System.out.println(list.get(1));
同样的代码,只不过给List加上了泛型,编译器就会在编译期自动检查代码是否符合要求并指出错误。提高了代码的正确性与安全性,并且在我们取数据是不用进行强制类型转换的,编译器已经知道它是String类型了。
3.应用
泛型的应用最常见的就是在类与方法上的应用。在这里简单说一下,首先了解一下泛型中的几个东西,extends、super、?,相信这几个东西是大家最不好理解的了。extends、super用于定义有界类型,extends用于定义类型的上限,表明参数化的类型可能是所指定的类型或者所指定类型的子类型;super定义类型的下限,表名参数化的类型可能是所指定的类型或者所指定的类型的父类型,一直到Object,因为Object为所有类型的基类。“?”定义不确定的类型,表示当前类型可能是任何一种类型。我会在下边的讲解中涉及到,大家慢慢理解。
首先我们来定义几个类:
public class Person { public String name; public Person(String name) { this.name = name; } public void printName() { System.out.println("Log_" + name); } } public class BlackPerson extends Person{ public BlackPerson(String name) { super(name); // TODO Auto-generated constructor stub } } public class YellowPerson extends Person{ public YellowPerson(String name) { super(name); // TODO Auto-generated constructor stub } } public class Zhangsan extends YellowPerson{ public Zhangsan(String name) { super(name); // TODO Auto-generated constructor stub } }我们定义了四个类,一个Person类做为顶级父类,两个Person直接子类BlackPerson、YellowPerson,还有一个YellowPerson的子类Zhangsan,这是他们的层级关系。我们在定义一个普通的方法:
public void settingPerson1(List<Person> list) { for (Person p : list) { p.printName(); } }我们进行下面编码:
Main m = new Main();//代码所在编译环境,包含Main方法。 List<Person> myList1 = new ArrayList<Person>(); myList1.add(new YellowPerson("Yellow Man")); myList1.add(new BlackPerson("Black Man")); m.settingPerson1(myList1);这种是最常见的用法,我们经常在开发中用到,不会有任何问题,看一下输出内容:
Log_Yellow Man Log_Black Man下边看一下我们在方法里用“?”与extends的方式,这个只是普通方法,不是泛型方法,泛型方法有它固定的书写方式,看方法:
public void settingExtends(List<? extends Person> list) { for (Person p : list) { p.printName(); } }实现代码:
List<BlackPerson> myList2 = new ArrayList<BlackPerson>(); myList2.add(new BlackPerson("Black Man1")); myList2.add(new BlackPerson("Black Man2")); m.settingExtends(myList2); List<YellowPerson> myList22 = new ArrayList<YellowPerson>(); myList22.add(new YellowPerson("Yellow Man1")); myList22.add(new BlackPerson("Black Man")); m.settingExtends(myList22);这两种情况两个不会出现问题(第二种将第二个加入的BlackPerson改为YellowPerson),注意一下list指定的泛型类型,下边分析一下,“?”是指不确定的某种类型,extends表示定义上限类型,放到一起就是当前方法接收的List中都必须是Person或者其子类型。注意这里,而且我们的settingExtends()方法中的?不能改为T,因为?为不确定的多种类型,T为不确定的某一种类型。同样在类上边使用的泛型也不能改为?,如,public class Person<T>{},这里的T不能用?替代,当我们在类上定义一个T时,这个T类型可以在整个类中使用,都表示相同的某种类型。
我们在方法上稍作修改,如下:
public void settingExtends(List<? extends YellowPerson> list) { for (Person p : list) { p.printName(); } }将extends Person改成extends YellowPerson,再写一段实现代码:
List<Person> myList3 = new ArrayList<Person>(); myList3.add(new Person("Person")); myList3.add(new YellowPerson("Yellow Man")); myList3.add(new BlackPerson("Black Man")); m.settingExtends(myList3);//编译出错。
这里就很好理解了,虽然我们在定义myList3时指定了Person类型,但是我们在方法里定义的只接受YellowPerson类型或妻子类型,我们这里又把Person加进去了,所以编译就通过不了了。
下边接着看另一种方式:
List<? extends Person> myList5=new ArrayList(); myList5.add(new Person("Person"));//编译不通过 myList5.add(new Zhangsan("张三"));//编译不通过 myList5.add(new YellowPerson("Yellow Man"));//编译不通过上边代码我们定义了一个接收所有Person子类的一个List,并且向里边添加的都是Person的子类,这在我们理解着应该是完全没问题的,但是其实不对,编译通过不了,你会发现add方法被画上了红线,为什么呢?“?”代表的是不确定的某种类型,可能是Person类型,也可能是YellowPerson类型或者BlackPerson与Zhangsan类型,当我们加入YellowPerson类型时,他有可能是BlackPerson类型,因为他的类型是不确定的某一种类型,所以我们不能向里边添加任何数据,这里需要你好好理解理解。
下边看一下super定义类型下限,先定义一个方法:
public void settingSuper(List<? super YellowPerson> list) { for (Object p : list) { System.out.println("Log_Class:"+p.getClass().getSimpleName()); } }Super表示只接受自身或者父类类型,我们定义了一个只接受YellowPerson及其父类的方法,并打印了类名,看一下实现:
List<Person> myList4 = new ArrayList<Person>(); myList4.add(new Zhangsan("张三")); myList4.add(new YellowPerson("Yellow Man")); myList4.add(new Person("Person")); m.settingSuper(myList4);当你写完上边代码运行:
Log_Class:Zhangsan Log_Class:YellowPerson Log_Class:Person你会惊奇的发现竟然没有问题,还能打印出信息,代码加入了YellowPerson的子类Zhangsan竟然没有报错,为什么呢?因为我们知道Java是可以向上转型的,我们可以把Zhangsan看成是YellowPerson,如下:
YellowPerson y= new Zhangsan();所以没有报异常。也就是说这里你可以把跟YellowPerson有直属关系的属于Person子类的(List<Person>,这里是Person子类)都加进去。
我们再看一下不调用方法,直接List实现:
List<? super YellowPerson> myList6=new ArrayList(); myList6.add(new Zhangsan("张三")); myList6.add(new YellowPerson("Yellow Man")); myList6.add(new Person("Person"));//编译报错;你是不是感觉这里也不应该出现问题,而实际上在代码最后一行报错了,为什么呢?因为跟上边extends时类似,Zhangsan与YellowPerson都可以看成YellowPerson。而“?”为一种不确定的类型,所以我们也不能添加数据,这里大家理解一下。
再看一下无界限类型:
List<?> myList7=new ArrayList(); myList7.add(111);//同样不知道里边具体是什么类型,不能添加数据; myList7.add("ssssss");//编译出错; myList7.add(new Person("Person"));//编译出错;同样无界限类型也是不能添加数据的。
如果我们指定了一种特定类型的泛型,比如下边List<Object>,那么我们是可以添加数据的,只要是符合类型:
public void settingWujie(List<?> list) { for (Object p : list) { System.out.println("Log_Class:"+p.getClass().getSimpleName()); }} List<Object> myList8=new ArrayList<Object>(); myList8.add(new Integer(5)); myList8.add(new Person("Person")); myList8.add(new YellowPerson("Yellow Man")); myList8.add(new BlackPerson("Black Man")); myList8.add(new Zhangsan("张三")); m.settingWujie(myList8);以上代码都是没有问题的,看输出;
Log_Class:Integer Log_Class:Person Log_Class:YellowPerson Log_Class:BlackPerson Log_Class:Zhangsan三、注意点
(1)泛型只针对类类型的参数,不针对基本数据类型,如:int、long等;
(2)T可以在类中用,“?”不能;
(3)T代表某一种确定的数据类型,“?”代表不确定的数据类型;
(4)注意通配符“?”修饰时几种不能直接进行add操作的情况;
(5)泛型参数可以不止一个,如<K,V>;
(6)泛型可以使用super,extends修饰,表示有界;“?”单独用表示无界限,如:List<?>,相当于list中可以放任何类型;
(7)List<Person>与ArrayList<YellowPerson>为两种不同类型,不能画等号。
(8)不能在静态域或普通静态方法中出现类上定义的泛型,因为静态域执行时间要早于类的构建,泛型方法可以;
(9)定义泛型方法时,泛型类型要在public(static)后返回值前,如:
public static <T> void setName(){...}
public static void <T> setName(){...} //错误
(10)泛型方法中用到的泛型类型,如果没有在类上定义,必须在方法上定义,如下:
public class Person<T>{
public <M> void setName(){
M a;
}
}
(11)类上定义的泛型作用于整个类,方法上定义的泛型,只在当前方法有效。
希望您看完本篇文章,能够对于泛型有一个全新的了解。
小例子下载地址:http://download.csdn.net/detail/liuyonglei1314/9778992