泛型入门
在没有泛型之前,Java集合有个缺点,当我们把一个对象丢进集合里后,集合就会忘记这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了Object类型(其运行时类型没变)
什么时候使用泛型?
当操作的引用数据类型不确定的时候
泛型的好处?
将运行时期的问题,ClassCastException转到了编译时期
避免了强制类型转换的麻烦和代码臃肿
import java.util.List;
import java.util.ArrayList;
/**
不使用泛型的集合容易引起ClassCastException
*/
class ListError
{
public static void main(String[] args)
{
//创建一个只想保存字符串的List集合
List strList = new ArrayList();
strList.add("aaaa");
strList.add("bbbb");
strList.add("cccc");
//不小心把一个Integer对象丢进了集合
strList.add(6);
for(int i = 0; i < strList.size(); i++)
{
//因为List里取出的全部是Object,所以必须强制类型转换
//最后一个元素将出现ClassCastException异常
String str = (String)strList.get(i);
}
}
}
泛型术语
泛型术语,以ArrayList<E>为例:
整个ArrayList<E>称为:泛型类型
ArrayList称为:原始类型(raw type)
<>念:typeof
ArrayList<E>中的E称为:类型参数或类型变量
整个ArrayList<Integer>称为:参数化的类型(ParameterizedType)
ArrayList<Integer>中的Integer称为:实际类型参数或类型参数的实例
泛型的注意事项
泛型不考虑类型参数的继承关系
ArrayList<String> a = new ArrayList<Object>(); //错误,不写<Object>可以(兼容老版本),写了就是明知故犯
ArrayList<Object> a = new ArrayList<String>(); //错误
泛型的类型参数只表示引用数据类型
import java.util.ArrayList;
/**
泛型的内部原理
*/
class GenericTest
{
public static void main(String[] args) throws Exception
{
ArrayList<Integer> al1 = new ArrayList<Integer>();
ArrayList<String> al2 = new ArrayList<String>();
//证明泛型只存在于源文件
System.out.println(al1.getClass() == al2.getClass());
//利用反射向集合中添加非泛型限定的对象
al1.getClass().getMethod("add", Object.class).invoke(al1, "abc");
System.out.println(al1.get(0));
}
}
泛型类
当一个类不确定要处理的引用类型数据时,可以定义泛型类,在创建对象时指定具体类型
/**
定义并使用泛型类
*/
//定义GenericClass时使用了泛型声明
class GenericClass<T>
{
//使用类型参数T定义属性
private T info;
public GenericClass(){}
//使用类型参数T作为构造函数的形参
public GenericClass(T info)
{
this.info = info;
}
//使用类型参数T作为方法的形参
public void setInfo(T info)
{
this.info = info;
}
public T getInfo()
{
return this.info;
}
}
//测试泛型类
class GenericClassTest
{
public static void main(String[] args)
{
//实际类型参数是String,所以构造器的形参是String类型
GenericClass<String> a1 = new GenericClass<String>("泛型类");
System.out.println(a1.getInfo());
//实际类型参数是Double,所以构造器的形参是Double类型
GenericClass<Double> a2 = new GenericClass<Double>(5.67); //自动装箱
System.out.println(a2.getInfo());
}
}
泛型方法
/**
泛型方法
*/
class GenericMethod<T>
{
//在方法中使用类型参数T作为形参,当创建Generic对象的时候,T被确定,而方法只能接收被确定的T类型
public void print(T msg)
{
System.out.println(msg);
}
//如果在方法上定义泛型,就不存在上面的问题
public <E> void show(E msg)
{
System.out.println(msg);
}
}
class GenericMethodTest
{
public static void main(String[] args)
{
GenericMethod<String> gm = new GenericMethod<String>();
gm.print("hello"); //print方法此时只能接收字符串参数
gm.show(123456); //方法上定义泛型,不受类上泛型的影响
}
}
在静态方法上使用泛型
/**
泛型类上的类型参数T只有在建立对象时才会被确定,所以无法在静态方法和属性上使用T
如果要在静态方法上使用泛型,就不可以使用泛型类上的类型参数,可以将泛型定义在静态方法上
*/
class GenericMethod<T>
{
//下面代码错误,无法在静态中访问非静态T
//public static T info;
//下面代码错误,无法从静态方法中访问非静态T
//public static void setInfo(T msg){};
//下面代码是允许的,因为在使用静态方法时就可以确定泛型E
public static <E> void print(E msg)
{
System.out.println(msg);
}
}
class GenericMethodTest2
{
public static void main(String[] args)
{
GenericMethod.print("hello");
}
}
泛型接口
/**
泛型接口
*/
//泛型接口
interface GenericInterface<T>
{
public void show(T t);
}
//实现接口,可以指定泛型接口的类型参数(另见例子2)
class GenericInterfaceImpl implements GenericInterface<String>
{
public void show(String t)
{
System.out.println(t);
}
}
//测试
class GenericInterfaceTest
{
public static void main(String[] args)
{
GenericInterfaceImpl gifi = new GenericInterfaceImpl();
gifi.show("hello");
}
}
泛型限定
通配符:<?>
上限定:<? extends E>
限定类型参数只能是E或E的子类
下限定:<? super E>
限定类型参数只能是E类型或E类型的父类型
如TreeSet<E>构造方法:TreeSet(Comparator<? super E> comparator)
限定Comparator的类型参数只能为E或者E的父类
import java.util.ArrayList;
import java.util.Iterator;
/**
上限定:<? extends E>
下限定:<? super E>
使用下限定时,由于无法知道父类中存在什么特有方法,所以?代表的是Object类型,如下:
*/
class Person
{
private String name;
Person(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
}
//Student继承Person
class Student extends Person
{
Student(String name)
{
super(name);
}
public void show()
{
System.out.println("hello");
}
}
class Wildcards2
{
public static void main(String[] args)
{
ArrayList<Person> al1 = new ArrayList<Person>();
al1.add(new Person("person1"));
al1.add(new Person("person2"));
al1.add(new Person("person3"));
print(al1);
ArrayList<Student> al2 = new ArrayList<Student>();
al2.add(new Student("student1"));
al2.add(new Student("student2"));
al2.add(new Student("student3"));
print(al2);
System.out.println("------------");
print2(al1);
print2(al2);
}
//限定ArrayList集合中的类型参数只能是Person或Person的子类
public static void print(ArrayList<? extends Person> al)
{
Iterator<? extends Person> it = al.iterator();
while(it.hasNext())
{
System.out.println(it.next().getName());
}
}
//限定ArrayList集合中的类型参数只能是Student或Student的父类
//但取出集合中的元素的类型是Object,无法使用Student或者其父类的特有方法
public static void print2(ArrayList<? super Student> al)
{
Iterator<? super Student> it = al.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
}
---------------------- ASP.Net+Android+IOS开发</a>、 .Net培训、期待与您交流! ----------------------