首页 > 代码库 > 黑马程序员-张老师基础5-泛型
黑马程序员-张老师基础5-泛型
泛型:
泛型只是提供给编译器。在编译完之后,生产字节码文件时就擦除类型信息,<>泛型就不在了
泛型的术语:
整个Arraylist<E>:称为泛型类型。
Arraylist<E>中的E称为类型变量或类型参数
整个Arraylisst<Integer>称为:参数化的类型
Arraylist<Integer>中的Integer称为类型参数的实例。
Arraylist<Integer>中的<>是typeof
Arraylist称为原始类型。
参数化类型与原始类型的兼容性:
--参数化类型可以引用一个原始类型的对象,会警告。例如:
--Collection<Integer> c = new Vector();
--Collection c = new Vector<String>();同样警告
参数化类型不考虑类型参数<E>的继承关系。
Vector<String> v = new Vector<Object>();错误,因为V get的元素必须是String的,如果get的是Object的,那泛型有神马用。如果Object不写,可以
Vector<Object> v = new Vector<String>()//难理解,挺难
思考题:下面的代码会报错吗,
Vector v1 = new Vector<Stirng>();
Vector<Object> v = v1;//V1此时仍是原始类型。不报错。编译器只会一行一行的扫描。
?通配符
Collection<String> coll1= new ArrayList<String>();
coll1.add("hahah");
private static void pringtCollection(Collection<?> coll) {
// 如何打印<?>中的对象
//coll.add("abc");//不行,因为要传的参数不确定
for(Object obj:coll){//不关什么都是object
System.out.println(obj);
coll = new HashSet<Date>();//这个可以,==赋值动作。
// coll.add("String");错误因为,前面是?,类型不确定,不能先定义好类型。但是用反省<T>就可以.
使用?通配符可以引用其他各种参数的类型,?通配符定义的变量主要用作引用,可以调用与参数无管方法
不可以调用与参数有关的方法。
coll.size();//可以,与参数无关。
}
}
<T>与通配符?的区别:<T>在里面可以添加元素等操作。
private static<T> void pringtCollection(Collection<T> coll,T objt) {
for(Object obj:coll){//不管什么对象都是object
System.out.println(obj);
coll = new HashSet<T>();//这个可以,==赋值动作
coll.add(objt);//与通配符?的区别
coll.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);
*提示:限定通配符总是包括自己。
自定义泛型方法:
用于放置泛型的类型参数的尖括号应出现在方法的返回类型之前。按照惯例,类型参数通常用单个大写字母表示。
*用一个面试题讲:把一个数组中的元素的顺序颠倒一下
swap(new String[]{"fdf","dfs","fdfdf"},0,1);
//错误 swap(new int[]{2,1,2,5},1,2);//int[]{},不可以是int,必须是引用型。
}
private static<T> void swap(T[] t,int i,int j) {
// TODO Auto-generated method stub
T tem = t[i];
t[i] = t[j];
t[j]=tem;
}
泛型的使用只能是引用型类型。不能是基本数据类型。
不仅在使用的时候可以使用extends ,在定义是也可以。如:public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
并且可以使用&来指定多边界,如:<V extends Serializable&cloneable> void method(){}
*普通方法,构造方法,静态方法都可以加泛型。
*可以用泛型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch语句中。
show()<T extends Exception>throws T
{
try
{
}
catch (Exception e)//这个Exception不可以用T表示。
{
throw<T>
}}
*在<>可以指定多个变量,用逗号隔开。
Integer[] arr= new Integer[4];
Integer[] arr1 = new Integer[5];
copyArray1(arr1,arr);
// copyArray1(new Date[10],new Integer[10]);注意这个也可以,它会推断出一个交集
// copyArray1(new Vecotr<Date>[10],new Integer[10]);这个则不可以。
private static<T> void copyArray1(T[] dest,T[] orig) {
// TODO Auto-generated method stub
for(int x = 0 ;x<orig.length;x++){
dest[x]=orig[x];
} }
*自定义泛型类
public class GenericDao<E> {
public void add(E x){}
public E findById(int id){
return null;
}
public void delete(E obj){}
public void update(E obj){}
public Set<E> findByconditions(String whrer){
return null;
}
Dao:data access object 对象访问数据。
//public static void show(E e){}静态方法能使用泛型类型的变量,对象的方法,静态不创建对象。静态方法只能自己定义,
如果类中有多个方法要使用泛型,就用类级别。
泛型类:写在类名后面。泛型方法:写在void前面。
泛型类:
在整个类中有效,如果被方法使用,一旦对象类型明确,所有方法的类型已经明确。
泛型的弊端:创建对象时,类<T>已经明确了类型,而定义方法上的T实际上已经被明确了,只能跟着对象走。那么如果想单独调用方法,传入两一个类型E,此时便受到了限制。想要 更灵活就定义泛型方法。
泛型方法:
为了让不同方法可以操作不同类型,如<E> void show(E e)和<T> void method(T t),那么就可以将泛型定义在方法上,而且两个方法可以互补影响。
静态泛型方法:
静态方法不可以访问类上定义的泛型(如在静态方法中打印类上的泛型<T> t)。这时静态加载,还未加载,报错。此时可以使用静态泛型。
如果静态方法操作的应用数据类型不明确,可以将泛型定义在方法上。
通过泛型的反射获取一个集合中的具体类型
import java.util.Vector;
public class Demogeneric {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Vector<Date> v = new Vector<Date>();
//如何通过反射获取一个集合中的具体泛型类型
//因为泛型在编译器之后会被擦出,所以无法通过V的字节码文件来获取泛型的类型。字节码中没有泛型。
//但是将V传递给方法,并通过获取方法上的参数列表来获取。
applyVector(v);
Method applyMethod = new Demogeneric().getClass().getMethod("applyVector", Vector.class);
Type[] types = applyMethod.getGenericParameterTypes();
for(Type t:types){ System.out.println(t);//java.util.Vector<java.util.Date>
}}
public static void applyVector(Vector<Date> d){
}}