泛型
泛型:JDK1.5版本以后出现的新特性。用于解决安全问题,是一个类型安全机制。
好处
1.将运行时期出现的问题ClassCastException转移到了编译时期。
方便与程序员解决问题。让运行时期减少问题。
2.避免了强制转换的麻烦
泛型格式:通过<>来定义要操作的引用数据类型。
在使用java提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见。
只要见到<>就要定义泛型。
其实<>就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到<>即可
传入
引用数据类型
当类中要操作的引用数据类型不确定的时候
早期定义Object来完成扩展
现在定义泛型来完成扩展。
泛型类定义的泛型,在整个类中有效。如果被方法使用,
那么泛型类的对象明确要操作的具体类型后,所要操作的类型就已经固定了
为了让不同方法可以操作不同类型,而且类型还不确定。
那么可以将泛型定义在方法上。
特殊之处
静态方法不可以访问类上定义的泛型。
如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上
class Demo1
<T
>
//类上的泛型
{
public void show(T t)
{
System.out.println(t);
}
public <Q > void print(Q q) //非静态方法上的泛型
{
System.out.println(q);
}
public static <W > void method(W t) //静态方法上的泛型
{
System.out.println( "method" + t);
}
}
public class GenericDemo {
public static void main(String[] args) {
Demo1 <Integer > d = new Demo1 <Integer >();
d.show( 5);
d.print( 4324);
Demo1 <String > d1 = new Demo1 <String >();
d1.show( "haha");
// d1.show(new Integer(2)); 编译错误,类型不匹配,此处用的类泛型,参数必须是String类型
//以下是方法的泛型,所以可以不是String类型
d1.print( "hahahaha");
d1.print( 4);
d1.method( 4);
}
}
{
public void show(T t)
{
System.out.println(t);
}
public <Q > void print(Q q) //非静态方法上的泛型
{
System.out.println(q);
}
public static <W > void method(W t) //静态方法上的泛型
{
System.out.println( "method" + t);
}
}
public class GenericDemo {
public static void main(String[] args) {
Demo1 <Integer > d = new Demo1 <Integer >();
d.show( 5);
d.print( 4324);
Demo1 <String > d1 = new Demo1 <String >();
d1.show( "haha");
// d1.show(new Integer(2)); 编译错误,类型不匹配,此处用的类泛型,参数必须是String类型
//以下是方法的泛型,所以可以不是String类型
d1.print( "hahahaha");
d1.print( 4);
d1.method( 4);
}
}
泛型定义在接口上
interface Inter <T >
{
void show(T t);
}
class InterImpl <T > implements Inter <T >
{
public void show(T t)
{
System.out.println( "show:" +t);
}
}
class GenericDemo5
{
public static void main(String[] args)
{
InterImpl <Integer > i = new InterImpl <Integer >();
i.show( 44);
}
}
类型参数的类型推断
编译器判断泛型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,
其实现方法是一种非常复杂的过程
根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
1.当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定
,这很容 易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4) -> static <E> void swap(E[] a,int i,int j)
2.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应到了
同一种类型来确定,这很容易凭感觉推断出来,例如:
add(3,5) -> static <T> T add(T a,T b)
3.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这么多处的
实际应用类型对应到了
不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例,下面语句实际对应的类型就是Number了,编译没问题
, 只是运行时出问题:
fill(new Integer[3],3,5f) -> static <T> void fill(T[] a,T v)
4.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不
同的类型,并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误
,将变量 x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有错误了:
int x=add(3,3.5f) -> static <T> T add(T a,T b)
5.参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没问题,而第二种情况则根据参数化的
Vector类实例将类型变量直接确定为String类型,编译将出现问题:
copy(new Integer[5],new String[5]) -> static <T> void copy(T[] a,T[] b)
copy(new Vector<String>(),new Integer[5]) -> static <T> void copy(Collection<T> dest,T[] src)
泛型限定
? 通配符。也可以理解为占位符。
问题:
定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
错误方式:
public static void printCollection(Collection<Object> cols)
{
for(Object obj:cols){
System.out.println(obj);
}
/*
cols.add("string");//没错
cols=new HashSet<Date>();//会报告错误
*/
}
正确的方式:
public static void printCollection(Collection<?> cols)
{
for(Object obj:cols){
System.out.println(obj);
}
//cols.add("string");//错误,因为它不知自己未来匹配就一定是String
cols.size();//没错次方法与类型参数没有关系
}
总结:
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。
泛型的限定;
限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误: Vector<? extends Number> x = new Vector<String>();
限定通配符的下边界
正确: Vector<? super Integer> x = new Vector<Number>();
错误: Vector<? super Integer> x = new Vector<Byte>();
提示:限定通配符总是包括自己
class Person3
{
private String name;
Person3(String name)
{
this.name =name;
}
public String getName()
{
return name;
}
}
class Student3 extends Person3
{
Student3(String name)
{
super(name);
}
}
public class GenericDemo2 {
public static void main(String[] args) {
ArrayList <Person3 > al = new ArrayList <Person3 >();
al.add( new Person3( "a1"));
al.add( new Person3( "a2"));
al.add( new Person3( "a3"));
al.add( new Person3( "a4"));
printColl(al);
ArrayList <Student3 > al1 = new ArrayList <Student3 >();
al1.add( new Student3( "a--1"));
al1.add( new Student3( "a--2"));
al1.add( new Student3( "a--3"));
al1.add( new Student3( "a--4"));
printColl(al1); //ArrayList<? extends Person3> al2 = new ArrayList<Student3>(); //正确
}
public static void printColl(ArrayList < ? extends Person3 > al)
{
Iterator < ? extends Person3 > it = al.iterator();
while(it.hasNext())
{
System.out.println(it.next().getName());
}
}
}
{
private String name;
Person3(String name)
{
this.name =name;
}
public String getName()
{
return name;
}
}
class Student3 extends Person3
{
Student3(String name)
{
super(name);
}
}
public class GenericDemo2 {
public static void main(String[] args) {
ArrayList <Person3 > al = new ArrayList <Person3 >();
al.add( new Person3( "a1"));
al.add( new Person3( "a2"));
al.add( new Person3( "a3"));
al.add( new Person3( "a4"));
printColl(al);
ArrayList <Student3 > al1 = new ArrayList <Student3 >();
al1.add( new Student3( "a--1"));
al1.add( new Student3( "a--2"));
al1.add( new Student3( "a--3"));
al1.add( new Student3( "a--4"));
printColl(al1); //ArrayList<? extends Person3> al2 = new ArrayList<Student3>(); //正确
}
public static void printColl(ArrayList < ? extends Person3 > al)
{
Iterator < ? extends Person3 > it = al.iterator();
while(it.hasNext())
{
System.out.println(it.next().getName());
}
}
}