一、为什么要引入泛型?以及泛型的本质?
(1)所谓的要引入泛型,就是创造容器类,这个容器类就是放入要使用对象的地方,而且这个容器比较灵活,可以放任意类型的对象,所以使用泛型的主要目的之一就是指定容器中持有什么类型的对象,而且编译器来保证类型的正确性。
(2)知道了为什么要引入泛型,我们就来认识一下泛型的本质:实质就是参数化类型,也就是说将数据类型作为参数,在使用的时候再指定具体数据类型。
(3)引入泛型的好处:第一可以检查检查数据类型,第二获取数据时自动进行强制类型转换。(其过程只发生在编译阶段);
重点内容介绍完为什么引入泛型,一个很重要的问题就是泛型类型的擦除?
(1)所谓的JAVA泛型擦除就是就是把所有的泛型擦除成原始的object类型;但是如果指定上界的话,就擦除到这个上界;
例如:
Class c1=new ArrayList<String>().getClass();
Class c2=new ArrayList<Integer>().getClass();
System.out.println(c1==c1);
结果返回的是true;因为它们都是相同的类型object;根据这一信息,我们可以看到java泛型是使用擦除来实现的,这意味着当你在使用泛型时,类型信息就被擦除掉了,你唯一知道的就是你在使用一个对象。
(2)有时我们需要根据情况需要,不想擦除到原始类型,因此就需要规定上界,擦除到这个指定的边界,用extends关键词来指定。
例如:
class GenericFun<T extends Comparable<T>>{
}
这里讲上界指定到Comparable,即在编译时擦除到Comparable
二、泛型的应用
(1)泛型类
eg: Class Array{ };
注意: T中不可以是简单类型;在使用的时候需指定类型参数;
class GenericMath<T extends Number>{
public int sumFunc(T[] array){
int sum = 0;
for(T val : array){
sum += val.intValue();
}
return sum;
}
(2)泛型方法
eg: public void ( T[] arrray){
}
注意:泛型方法是在参数列表中返回值前面加泛型参数列表;
泛型方法举例1:
public static <E extends Number> int sumFunc2(E[] array){
int sum = 0;
for(E val : array){
sum += val.intValue();
}
return sum;
}
泛型方法举例2
//利用Comparable接口创建自己的类进行比较,即实现compareTo方法,
public static <E extends Comparable<E>> E min(E[] array){
E minval=array[0];
for(int i=0;i<array.length;i++){
if(minval.compareTo(array[i])>0){
minval=array[i];
}
}
return minval;
}
泛型方法举例3:
实现一个静态方法,比较任意类型两个元素的大小,返回boolean
//Integer/Integer Double/Double Short/Short
public static <E extends Comparable<E>> boolean compare(E a, E b){
return a.compareTo(b) > 0 ? true : false;
}
}
(3)泛型接口
eg: Comparable<Person>;
class Person implements Comparable<Person>{
private String name;
private int age;
private String sex;
public Person(String name, int age, String sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
}
public int age(){
return age;
}
//按姓名进行比较
@Override
public int compareTo(Person o) {
// TODO Auto-generated method stub
return name.compareTo(o.name);
}
}
//Student继承person
class Student extends Person{
private double score;
public Student(String name, int age, String sex, double score){
super(name, age, sex);
this.score = score;
}
}
class GenericFunctional{
//比较对象的大小 Comparable<Student>没有接口,但是继承了基类Comparable<Person>实现了接口
// compareTo(? obj)
public static <E extends Comparable<? super E>> boolean compareObject(E a, E b){
return a.compareTo(b) > 0 ? true : false;
}
//main函数中添加代码,定义两个student对象和一个比较器对象,进行学生之间的自定义比较
public static <E> boolean compareObject2(E a, E b, Comparator<? super E> c){ //利用比较器比较
return c.compare(a, b) > 0 ? true : false;
}
}
public class TestGenericDemo3 {
//定义三个匿名对象,专门对age进行排序
public static Comparator<Person> AGE_COMP = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
// TODO Auto-generated method stub
return o1.age() > o2.age() ? 1 : (o1.age() == o2.age() ? 0 : -1);
}
};
public static void main(String[] args) {
// TODO Auto-generated method stub
//生成两个Person对象,调用compareObject进行比较
Person p1 = new Person("zhang", 20, "male");
Person p2 = new Person("liu", 21, "female");
System.out.println(GenericFunctional.compareObject(p1, p2));
//生成两个Student对象,调用compareObject进行比较
Student s1 = new Student("zhang", 20, "male", 98.5);
Student s2 = new Student("zhang", 20, "male", 98.5);
System.out.println(GenericFunctional.compareObject(p1, p2));
重点内容
三、泛型常见错误
(1)不能直接new泛型类型的对象 因为编译器不知道泛型类型T到底是什么类型? T a = new T();(即在使用时需给定类型)
(2)不能直接new泛型类型的数组,因为编译器不知道T是什么类型 ?T[] arr = new T[5]; => T[] array = (T[])new Object[5];
(3)static方法不能使用泛型类定义的泛型参数T呢?
因为static不需要对象来调用,是用类名来调用,而且类名调用不加参数类型,所以没法检测类型。
(4)不能定义泛型类的数组
GenericMath[] g3 = new GenericMath[5];
java的数组也是继承关系:
object[]是基类,因此object[]可以引用任何数组,就会出现数组中可以赋任意类型元素的错误。
所以在new数组是需类型强转:
T[] array=(T [] ) new object[[size]
(5)泛型代码需要检查需要制定上界?
public static int sumFunc3(T[] array){ }
(6)两个引用类型互相为继承关系,本来可以基类引用派生类对象的, 但是,该两个类型作为泛型类的实例化类型,互相是不能够引用的!
四、java的泛型通配符
(1)泛型通配符有上界也有界;
重点内容
通配符的上界是进行读,读取的类型是object类型;
通配符的下界是进行写,写入元素的形参类型写为通配符的下界;
不能用通配符定义对象;
(2)上界的应用:
public static int sumNumberStack(SqStack<? extends Number> stack){
int size = stack.size();
int sum = 0;
for(int i=0; i<size; ++i){
sum += stack.get(i).intValue();
}
return sum;
}
}
(3)下界关键词super(意思是不能超过它,必须是它的基类
eg:
SqStack <? super Number> s=new SqStack<object>();
可以看到<? super Number>意思是一个未知的类型,但该类型是Number的基类,也就是说Number是该类型的下限