Java泛型中的标记符含义:
E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的java类型
S、U、V - 2nd、3rd、4th types
Object跟这些标记符代表的java类型有啥区别呢?
Object是所有类的根类,任何类的对象都可以设置给该Object引用变量,使用的时候可能需要类型强制转换,但是用使用了泛型T、E等这些标识符后,在实际用之前类型就已经确定了,不需要再进行类型强制转换。
Jdk 1.5 以前的集合类中存在什么问题
ArrayList collection = newArrayList();
collection.add(1);
collection.add(1L);
collection.add("abc");
int i = (Integer)collection.get(1);//编译要强制类型转换且运行时出错!
Jdk 1.5 的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加
入指定类型以外的数据
ArrayList<Integer>collection2 = new ArrayList<Integer>();
collection2.add(1);
/*collection2.add(1L);
collection2.add(“abc”);*///这两行代码编译时就报告了语法错误
int i2 = collection2.get(0);//不需要再进行类型转换
泛型是提供给javac 编译器使用的,
利用反射穿透泛型限制(暴力反射)
泛型能绝对保证集合中存入数据都是它限定的类型吗?先看下边的代码
package july78javaEnhance;
import java.util.ArrayList;
public class Demo23 {
public static void main(String[]args) {
ArrayList<Integer>collection2 = new ArrayList<Integer>();
System.out.println(collection1.getClass()==collection2.getClas
s());
collection2.add(“真暴力”);//这句会报错
collection2.getClass().getMethod("add",
Object.class).invoke(collection2,"真暴力");
System.out.println(collection2.get(0));//结果却为真暴力
//已经限制集合中元素的类型为Integer,可用反射却能将String存入,为什
么?这是因为泛型是给编译器用的,运行时就没有这些泛型信息了,这叫做“去泛型化”,
所以可以通过反射,获取集合字节码加入非指定的类型。
}
}
泛型中的?通配符的扩展
限定通配符的上边界:
正确: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>();
提示:
限定通配符总是包括自己。
?只能用作引用,不能用它去给其他变量赋值
Vector<? extends Number> y= new Vector<Integer>();
Vector<Number> x = y;
上面的代码错误,原理与Vector<Object> x11 = new Vector<String>();相似,
只能通过强制类型转换方式来赋值。
Eg:
泛型集合类的综合案例
能写出下面的代码即代表掌握了Java的泛型集合类:
HashMap<String,Integer> hm= new HashMap<String,Integer>();
hm.put("zxx",19);
hm.put("lis",18);
Set<Map.Entry<String,Integer>>mes= hm.entrySet();
for(Map.Entry<String,Integer>me : mes) {
System.out.println(me.getKey() +":" + me.getValue());
}
由C++的模板函数引入自定义泛型
如下函数的结构很相似,仅类型不同:
int add(int x,int y) {
return x+y;
}
float add(float x,float y) {
return x+y;
}
double add(double x,double y) {
return x+y;
}
C++用模板函数解决,只写一个通用的方法,它可以适应各种类型,示意代码如下:
template<class T>
T add(T x,T y) {
return (T) (x+y);
}
类型参数的类型推断(花了张老师两天的时间总结)
l 编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断
的,其实现方法是一种非常复杂的过程。
l 根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
1.当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据
调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法
时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4) à static<E> void swap(E[] a, int i, int j)
2.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方
法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:
add(3,5) à static <T> Tadd(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 =(3,3.5f) à static<T> T add(T a, T b)
5.参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为 Object,编译没
有问题,而第二种情况则根据参数化的Vector 类实例将类型变量直接确定为String 类型,编
译将出现问题:
copy(new Integer[5],newString[5]) à static <T> void copy(T[] a,T[] b);
copy(new Vector<String>(),new Integer[5]) à static <T> void copy(Collection<T> a , T[] b);
定义泛型类型
如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保
持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型,语
法格式如下:
public class GenericDao<T>{
private T field1;
public void save(T obj){}
public T getById(int id){}
}
类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下两
种方式都可以:
GenericDao<String> dao =null;
new genericDao<String>();
注意:
在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静
态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的
类型参数。
问题:类中只有一个方法需要使用泛型,是使用类级别的泛型,还是使用方法级别的泛
型?
(Create)、查询(Retrieve)(重新得到数据)、更新(Update)和删除(Delete)几个单词的首字母简
写
import java.util.Set;
//dao data access object(数据访问对象)--->crud
public class GenericDao<E>{
public void add(E x){
}
public E findById(int id){
return null;
}
public void delete(E obj){
}
public void delete(int id){
}
public void update(E obj){
}
public static <E> voidupdate2(E obj){
}
public E findByUserName(Stringname){
return null;
}
public Set<E>findByConditions(String where){
return null;
}
}