1、概述
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。在使用的时候,具体指定操作数据类型,这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。
2、泛型举例
例子1:
//泛型例子
public static void testGenerics(){
ArrayList list = new ArrayList();
list.add("nihao");
list.add(100);
for(int i =0;i<list.size();i++){
String content = (String)list.get(i);
System.out.println(content);
}
}
上述代码编译可以正常通过,但运行会报一下错误:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
说明:
因为ArrayList内部存储数据用的是Object数组,所以可以向ArrayList添加任意类型数据(String,Integer),所以在编译期间可以正常通过。但是再运行期间,由于添加了Integer数据,强制转换成String使用的时候就会报错。为了在编译期间(编译器做出错误提示)就可以解决此类问题,泛型便出现了。
我们将上述ArrayList 的声明初始化代码修改如下,编译器会在编译阶段就会发现类似这样的问题。
如上图,可以发现,eclipse做出了错误提醒。
例子2:
public static void testRunGenerics(){
List<String> list1 = new ArrayList<String>();
List<Long> list2 = new ArrayList<Long>();
System.out.println(list1.getClass().equals(list2.getClass()));
}
运行结果:true
上述例子说明,java代码在编译后,会去除泛型信息。也即是说,java中泛型只是在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型相关信息擦除,并在对象进入和离开方法的边界处,添加类型检查,和类型转换的方法。
一句话:泛型类型,只在编译阶段提供,类型信息,用于检查类型安全,和生成类型转换方法。编译成的class文件,其实都是相同的基本类型。
3、泛型的定义和使用
泛型有三种使用方式,泛型接口,泛型类,泛型方法;
泛型接口
Show.java文件
/**
*
*/
package generics;
/**
* @author chzhao
*
*/
public interface Show <T,M>{
public void show(T t,M m);
}
TestGeneric.java文件
package generics;
import java.util.Date;
//在类的定义的时候指定第一个参数为String,另一个参数不指定任何类型
class ShowTest <M> implements Show<String,M>{
@Override
public void show(String t, M m) {
// TODO Auto-generated method stub
System.out.println(t);
System.out.println(m);
}
}
public class TestGeneric {
public static void main(String[] args) {
//可以在使用的时候指定第二个参数类型。
//注意接口Show中指定参数,第一个参数必须和
//ShowTest定义指定的第一个参数类型相同,如不同编译器报错。
Show <String,Date>showTest = new ShowTest<Date>();
Date date = new Date();
showTest.show("nihao", date);
}
}
运行结果:
nihao
Sat Feb 24 15:01:17 CST 2018
泛型类
在类的定义中使用泛型,我们称这样的类为泛型类。jdk中最典型的的泛型类,如List Set Map。
泛型类的基本写法:
class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{
private 泛型标识 /*(成员变量类型)*/ var;
.....
}
}
TestGeneric.java文件
package generics;
import java.util.Date;
//在实例化泛型类时,必须指定V的具体类型
//此处V可以写成任意符合java规则的标识
class GenericValue <V>{
//value这个成员变量的类型为V,V的类型由外部指定
private V value;
//泛型构造方法,形参v的类型由该类具体使用时指定。
public GenericValue(V v){
this.value = v;
}
//泛型方法 返回值由该类具体使用时指定。
public V getValue(){
return value;
}
}
public class TestGeneric {
public static void main(String[] args) {
GenericValue<String> genericValue = new GenericValue<String>("weiwei");
System.out.println(genericValue.getValue());
GenericValue<Date> genericValue1 = new GenericValue<Date>(new Date());
System.out.println(genericValue1.getValue());
}
}
运行结果:
weiwei
Sat Feb 24 15:26:25 CST 2018
泛型类使用,记住一点在使用的时候(声明和实例化的时候)具体指定参数类型。
还有一点说明,定义了一个泛型类,我们在使用的时候指定具体类型,主要作用给编译器在编译java源代码的时候使用,可以起到类型安全检查,和类型强制转换代码的生成。但记住我们在使用的时候,也可以不指定具体类型(其实也就是放弃了使用泛型,也就享受不到泛型带来的好处了),这时候,代码也是可以编译通过运行的,如下面例子。
public class TestGeneric {
public static void main(String[] args) {
GenericValue genericValue = new GenericValue("weiwei");
System.out.println(genericValue.getValue());
GenericValue genericValue1 = new GenericValue(new Date());
System.out.println(genericValue1.getValue());
}
}
运行结果:
weiwei
Sat Feb 24 15:26:25 CST 2018
泛型方法
/**泛型方法
* @param T 声明一个泛型类型
* @param c 用来创建泛型对象
* @return T 返回一个泛型类型的返回值
* @throws InstantiationException
* @throws IllegalAccessException
*/
//<T> 此作用说明该方法为泛型方法
//参数(Class<T>)指定此方法的具体泛型类型
public <T> T getObject(Class<T> c) throws InstantiationException, IllegalAccessException{
T t = c.newInstance();
return t;
}
测试代码TestGeneric.java文件
package generics;
import java.util.Date;
class Peoples{
private String name = "张三";
public void showName(){
System.out.println(name);
}
}
//在实例化泛型类时,必须指定V的具体类型
//此处V可以写成任意符合java规则的标识
class GenericValue <V>{
//value这个成员变量的类型为V,V的类型由外部指定
private V value;
//泛型构造方法,形参v的类型由该类具体使用时指定。
public GenericValue(V v){
this.value = v;
}
//泛型方法 返回值由该类具体使用时指定。
public V getValue(){
return value;
}
/**泛型方法
* @param T 声明一个泛型类型
* @param c 用来创建泛型对象
* @return T 返回一个泛型类型的返回值
* @throws InstantiationException
* @throws IllegalAccessException
*/
public <T> T getObject(Class<T> c) throws InstantiationException, IllegalAccessException{
T t = c.newInstance();
return t;
}
}
public class TestGeneric {
public static void main(String[] args) {
GenericValue <String>genericValue = new GenericValue<String>("weiwei");
try {
Peoples people = genericValue.getObject(Peoples.class);
people.showName();
} catch (InstantiationException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:张三
静态泛型方法
import java.util.ArrayList;
import java.util.List;
public class TestGeneric {
//打印容器泛型方法
public static <T> void printList(List<T> t){
for(T tt:t){
System.out.print(tt+"\t");
}
System.out.println();
}
public static void main(String[] args) {
List<Integer> intList = new ArrayList<Integer>();
List<String> stringList = new ArrayList<String>();
for(int i =0;i<10;i++){
intList.add(i);
stringList.add("weiwei"+i);
}
//打印list容器
printList(intList);
printList(stringList);
}
}
运行结果:
泛型方法能使方法独立于类而产生变化,对于泛型方法的使用有一个指导原则:如果你能做到,你就该尽量使用泛型方法。也就是说,如果使用泛型方法将整个类泛型化,那么就应该使用泛型方法。对于一个static的方法,无法访问泛型类型的参数。所以如果static方法要使用泛型能力,就必须使其成为泛型方法
希望对您有所帮助!