首页 > 代码库 > Java泛型

Java泛型

1.Java泛型概述
1.1什么是泛型
泛型(Generics)是对Java语言的类型系统的一种扩展,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
1.2为什么要使用泛型
在使用泛型前,存入集合中的元素可以是任何类型的,当从集合中取出时,所有的元素都是Object类型,需要进行强制类型转换,转换到特定的类型。这个强制类型转换可能会引起运行时的错误。
在使用泛型前,要实现不同参数类型,功能相同的方法需要为每种参数类型定义一个方法,这无疑是重复相同的工作。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
2.Java泛型规则和限制
1、泛型的类型参数只能是类类型(包括自定义类),不能是基本类型。
2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
3、泛型的类型参数可以有多个。
4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为“有界类型”。
5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String")。习惯上称为“有界类型”。
6、泛型不是协变的:List<Object>不是List<String>的父类型。
3.Java泛型的分类
泛型分为泛型接口,泛型类,泛型方法。
3.1泛型接口
<pre name="code" class="java">public interface GenericsTest<T> {
	public T show();
	
}
class Color1{
	public void play(){
		System.out.println("我是红色!");
	}
}
class Color2{
	public void play(){
		System.out.println("我是绿色!");
	}
}

3.2泛型类

public class GenericsTest<T> {
	private T color;
	public static void main(String[] args) {
		GenericsTest<Color1> generics1=new GenericsTest<Color1>();
		generics1.color.play();//输出:我是红色!
		GenericsTest<Color1> generics2=new GenericsTest<Color1>();
		generics2.color.play();//输出:我是绿色!
	}
}
class Color1{
	public void play(){
		System.out.println("我是红色!");
	}
}
class Color2{
	public void play(){
		System.out.println("我是绿色!");
	}
}

PS:实际上并不存在泛型类,不管泛型类型形参传入哪一种类型参数,对于Java来说它们依然被当成同一个类处理,在内存中也只占用一块内存空间,因此在静态方法,静态初始化块或静态变量的声明或初始化中不允许使用类型形参。不允许使用list instanceof ArrayList<String>,因为ArrayList<String>不是类。

import java.util.ArrayList;
import java.util.List;
public class GenericsTest{
	public static void main(String[] args) {
		List<Integer> list=new ArrayList<Integer>();
		List<String> li=new ArrayList<String>();
		System.out.println(list.getClass());//输出class java.util.ArrayList
		System.out.println(list.getClass()==li.getClass());//输出true,说明它们是同一个类
	}
}

3.3泛型方法

public class GenericsTest{
	public static <T> void play(T x){
		System.out.println(x);
	}
	public static void main(String[] args) {
		Integer i=2014;
		String str="lavor_zl";
		play(i);//输出结果:2014
		play(str);//输出结果:lavor_zl
	}
}

4.泛型高级应用
4.1限制泛型
<T extends XXX>这里的限定使用关键字extends,后面可以是类也可以是接口。但这里的extends已经不是继承的含义了,应该理解为T类型是实现XXX接口的类型,或者T是继承了XXX类的类型。
当要求T继承一个类和实现若干个接口,或实现多个接口时,它们之间要用"&"分开,类要写在接口的前面。
<T extends SomeClass&Interface1&Interface2>
我们或许会想限制泛型可以可以让T继承XXX或YYY呢!
我们甚至会试试看,<T extends XXX,YYY>,虽然不会报错,但是YYY没有起作用,因为它被隐藏了。
4.2通配符
为了解决类型被限制死了不能动态根据实例来确定的缺点,引入了“通配符泛型”,使用通配泛型格式为<? extends XXX>,“?”代表未知类型,XXX代表一个类或一个接口。
1、如果只指定了<?>,而没有extends,则默认是允许Object及其下的任何Java类了。也就是任意类。
2、通配符泛型不单可以向下限制,如<? extends Collection>,还可以向上限制,如<? super Double>,表示类型只能接受Double及其上层父类类型,如Number、Object类型的实例。
3、泛型类定义可以有多个泛型参数,中间用逗号隔开,还可以定义泛型接口,泛型方法。这些都与泛型类中泛型的使用规则类似。
4.3擦除和转换
在严格的泛型代码中,带泛型声明的类总应该带着类型参数。但为了与老的Java代码保持一致,也允许在使用泛型声明的类时不指定实际的类型参数,此时,类型参数被称为原始类型(raw type),默认是声明该参数时指定的第一个上限类型。
当把一个具有泛型信息的对象赋值给另一个没有泛型信息的对象时,所有“<>”里面的类型信息都将被扔掉。这叫“擦除”。

import java.util.ArrayList;
import java.util.List;
public class GenericsTest{
	public static void main(String[] args) {
		List<Integer> list=new ArrayList<Integer>();
		list.add(2014);
		List li=list;//这就是擦除
	}
}

也可以把一个没有泛型信息的对象赋值给一个具有泛型信息的对象,但是若从具有泛型信息的对象中取出与泛型参数相关的数据时,会将该数据强制转换成泛型参数的类型,很有可能会引发ClassCastException异常。

import java.util.ArrayList;
import java.util.List;
public class GenericsTest{
	public static void main(String[] args) {
		List<Integer> list=new ArrayList<Integer>();
		list.add(2014);
		List li=list;
		List<String> l=li;
		li.get(0);//原来存放的数据取出来时被强制转换成String类型
	}
}

5.泛型与数组
可以使用带范型参数值的类声明数组,却不可有创建数组。
比如:只能声明List<String>[],但不可以创建ArrayList<String>[10]这样的对象数组。
Java允许创建无上限的通配符泛型数组。
比如:List<?> list=new ArrayList<?>[10]。

PS:List<String> list=new ArrayList<String>(),右边"<>"里面的类型必须和左边一致且不可以省略。

Java1.7以后右边"<>"里面的类型可以省略,这就是Java1.7泛型的"菱形"语法。



Java泛型