<p><span style="margin: 0px; padding: 0px; font-family: 宋体; ">
</span></p><div style="margin: 0px; padding: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; "><span style="margin: 0px; padding: 0px; font-family: 宋体; ">体验泛型</span>
</div><p style="margin-top: 1em; margin-bottom: 1em; padding-top: 0px; padding-bottom: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; "><span style="margin: 0px; padding: 0px; ">1. JDK 1.5</span><span style="margin: 0px; padding: 0px; font-family: 宋体; ">以前的集合类中存在的问题</span><span style="margin: 0px; padding: 0px; "></span></p><p style="margin-top: 1em; margin-bottom: 1em; padding-top: 0px; padding-bottom: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; "><span style="margin: 0px; padding: 0px; "> ArrayListcollection=new ArrayList();</span></p><p style="margin-top: 1em; margin-bottom: 1em; padding-top: 0px; padding-bottom: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; "><span style="margin: 0px; padding: 0px; "> collection.add(1);</span></p><p style="margin-top: 1em; margin-bottom: 1em; padding-top: 0px; padding-bottom: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; "><span style="margin: 0px; padding: 0px; "> collection.add(1L);</span></p><p style="margin-top: 1em; margin-bottom: 1em; padding-top: 0px; padding-bottom: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; "><span style="margin: 0px; padding: 0px; "> collection.add(“abc”);</span></p><p style="margin-top: 1em; margin-bottom: 1em; padding-top: 0px; padding-bottom: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; "><span style="margin: 0px; padding: 0px; "> int i=(Integer)arrayList.get(i);//</span><span style="margin: 0px; padding: 0px; font-family: 宋体; ">编译要强制类型转换且运行时出错</span><span style="margin: 0px; padding: 0px; ">
2. JDK 1.5</span><span style="margin: 0px; padding: 0px; font-family: 宋体; ">集合类希望你在定义集合时。明确表示你要向集合中装哪些类型的数据,无法加入指定类型以外的数据</span><span style="margin: 0px; padding: 0px; "></span></p><p style="margin-top: 1em; margin-bottom: 1em; padding-top: 0px; padding-bottom: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; "><span style="margin: 0px; padding: 0px; "> ArrayList<Integer>collection2=new ArrayList<Integer>();</span></p><p style="margin-top: 1em; margin-bottom: 1em; padding-top: 0px; padding-bottom: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; "><span style="margin: 0px; padding: 0px; "> collection2.add(1);</span></p><p style="margin-top: 1em; margin-bottom: 1em; padding-top: 0px; padding-bottom: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; "><span style="margin: 0px; padding: 0px; "> /*</span></p><p style="margin-top: 1em; margin-bottom: 1em; padding-top: 0px; padding-bottom: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; text-indent: 21pt; "><span style="margin: 0px; padding: 0px; ">collection2.add(1L);</span></p><p style="margin-top: 1em; margin-bottom: 1em; padding-top: 0px; padding-bottom: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; text-indent: 21pt; "><span style="margin: 0px; padding: 0px; ">collection2.add(“abc”);//</span><span style="margin: 0px; padding: 0px; font-family: 宋体; ">这两行代码编译时就报告了语法错误</span><span style="margin: 0px; padding: 0px; "></span></p><p style="margin-top: 1em; margin-bottom: 1em; padding-top: 0px; padding-bottom: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; text-indent: 21pt; "><span style="margin: 0px; padding: 0px; ">inti2=collection.get(0);//</span><span style="margin: 0px; padding: 0px; font-family: 宋体; ">不需要再进行类型转换</span><span style="margin: 0px; padding: 0px; "></span></p><p style="margin-top: 1em; margin-bottom: 1em; padding-top: 0px; padding-bottom: 0px; font-family: 微软雅黑, Tohoma; font-size: 14.399999618530273px; line-height: 17.600000381469727px; "><span style="margin: 0px; padding: 0px; ">3. </span><span style="margin: 0px; padding: 0px; font-family: 宋体; ">泛型是提供给</span><span style="margin: 0px; padding: 0px; ">javac</span><span style="margin: 0px; padding: 0px; font-family: 宋体; ">编译器使用的,可以限定集合中的输入类型,让编译器挡住元程序的非法输入,编译器编译带类型说明的集合时会去除掉“类型</span><span style="margin: 0px; padding: 0px; ">” </span><span style="margin: 0px; padding: 0px; font-family: 宋体; ">信息,程序运行效率不受影响,对于参数化的的泛型类型,</span><span style="margin: 0px; padding: 0px; ">getClass()</span><span style="margin: 0px; padding: 0px; font-family: 宋体; ">方法的返回值和原始类型完全一样,由于编译生成的字节码会去掉泛型的类型信息,只要能跳出编译器,就可以往某个泛型集合中加入其他类型的数据,例如,用反射得到集合,再调用其</span><span style="margin: 0px; padding: 0px; ">add</span><span style="margin: 0px; padding: 0px; font-family: 宋体; ">方法即可</span></p>
import java.util.ArrayList;
import java.util.Collection;
public class GeneicTest
{
public staticvoid main(String[] args)
{
ArrayListlist=new ArrayList();
list.add(1);
list.add(1L);
list.add("abc");
Objectobj=list.get(1);
System.out.println(obj);
ArrayList<Integer>list2=new ArrayList<Integer>();
list2.add(1);
// list2.add(1L);//报错
//list2.add("abc");//报错
Objectobj2=list2.get(0);
System.out.println(obj2);
}
}
泛型的内部原理及更深应用
ArrayList<Integer>list2=new ArrayList<Integer>();
list2.add(1);
// list2.add(1L);//报错
//list2.add("abc");//报错
Objectobj2=list2.get(0);
System.out.println(obj2);
System.out.println(list2.getClass()==list3.getClass());
list3.getClass().getMethod("add",Object.class).invoke(list2,"abc");//将字符串写进Integer泛型的集合中,说明编译后悔去掉泛型的类型信息
System.out.println(list2.get(1));//得到
了解泛型
1. ArryList<E>类定义和ArrayList<Integer>类引用中不涉及到如下术语
1.整个称为ArrayList<E>泛型类型
2.ArrayList<E>中的E称为参数化的类型
3.整个ArrayList<Interger>称为参数化的类型
4.ArrayList<Integer>中的Integer称为类型参数的实例或实例参数类型
5.ArrayList<Integer>中的<>念着typeof
6. ArrayList称为原始类型
2. 参数化类型与原始类型的兼容性
1.参数化类型可以引用一个原始的类型的对象,编译报告警告,例如
Collection<String>c=new Vector();
2.原始类型可以引用一个参数化的类型的对象,编译包啊哦警告,例如,
Collection<String>v=new Vector();
3.参数化类型不考虑类型参数的继承关系:
Vector<String> v=newVector<Object>();//错误!
Vector<Object> v=new Vector<String>();//也错误
4.在创建数组实例时,数组的元素不能使用参数后的类型,例如,下面语句有错误;
Vector<Integer> vector list[]=new Vector<Integer>(10);
泛型的通配符扩展应用
1.问题:
定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法是如何定义的呢?
2. 错误方式:
public staticvoid printCollection(Collection<Object> cols)
{
for(Object obj:clos)
{
System.out.println(obj);
}
/* cols.add(“string”);//没错
cols=new HashSet<Date>();//会报告错误!*/
}
3. 正确方式
public static voidprintCollection(Collection<?> cols)
{
for(Object obj:cols)
{
System.out.println(obj);
}
//cols.add(“string”);//错误,因为它不知道自己未来匹配就一定是String
cols.size();//没错,此方法与类型参数没有关系
cols=new HashSet<Date>();
}
总结:
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要作用是作引用,可以调用与参数化无关的方法,不能调用与参数有关的发那个发
package com.itcast.day2;
importjava.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import org.w3c.dom.ls.LSInput;
public class GeneicTest
{
public static void main(String[] args) throws Exception
{
ArrayListlist=new ArrayList();
list.add(1);
list.add(1L);
list.add("abc");
Objectobj=list.get(1);
System.out.println(obj);
ArrayList<Integer>list2=new ArrayList<Integer>();
list2.add(1);
// list2.add(1L);//报错
//list2.add("abc");//报错
Objectobj2=list2.get(0);
System.out.println(obj2);
ArrayList<String>list3=new ArrayList<String>();
list3.add("hello");
System.out.println(list2.getClass()==list3.getClass());
list3.getClass().getMethod("add",Object.class).invoke(list2,"abc");
System.out.println(list2.get(1));
printCollection(list2);
printCollection(list3);
}
//申明打印任何集合的方法,通配符,?表示可以指向任意类型
static void printCollection(Collection<?> cols)
{
for(Object obj:cols)
{
System.out.println(obj);
}
System.out.println(cols.size());
}
}
泛型中的?统配符的扩展
1. 限定通配符的上边界
1. 正确: Vector<?extends Number> x=new Vector<Integer>();
2. 错误:Vector<?extends Number> x=new Vector<String>();
2. 限定通配符的下边界
1. 正确: Vector<? superInteger> x=new Vector<Number>();
2.错误: verctor<? super Integer> x=new Vector<Byte>();
3. 提示
限定通配符总是包括自己
泛型集合的综合应用案例
HashMap<String, Integer> maps=new HashMap<String, Integer>();
maps.put("zhansan",34);
maps.put("lisi",34);
maps.put("wangwu",35);
Set<Map.Entry<String, Integer>>set=maps.entrySet();
for(Map.Entry<String,Integer> entry:set)
{
System.out.println(entry.getKey()+":"+entry.getValue());
}
对在JSP中经常要对Set或者map集合进行迭代
<c:forEach items=”${map}”var=”entry”>
${entry.key}:${entry.value}
</c:forEach>
自定义泛型方法及其应用
由c++的模板函数引入自定义泛型
1. 如下函数的结构很相似,仅仅类型不同
int add(int x,int y)
{
return x+y;
}
float add(floatx,float y)
{
return x+Y;
}
doubleadd(double x,double y)
{
return x+y;
}
2. c++用模板函数解决,只写了一个通用的方法,它可以适用各种类型示意代码如下:
template<class T>
T add(T x,T y)
{
return (T)(x+y);
}
java自定义泛型
1. Java的泛型方法没有c++模板函数功能那么强大,java中的如下代码吴发通告编译
<T>T add(T x,T y)
{
return (T)(x+y);
//return null;
}
2. 交换数组中的两个元素的位置的泛型方法定义如下:
static <E> void swap(E[] a,int x,int j)
{
E t=a[i];
a[i]=a[j];
a[j]=t;
}
3. 用于放置泛型的类型参数的尖括号应该出现在方法的其他所有修饰符后,和在方法的返回类型之前,也就是紧邻返回值之前,按照惯例,类型参数通常用单个大写字母白表示
4.只有引用类型才能作为泛型的方法的时间参数,swap(newint[3],3,5);语句会报告编译错误
5.除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,例如,Class.getAnnotation()方法的定义,并且可以用&来指定多个边界,如<V extends Serlizabel
& cloneable> viod method(){};
6. 普通方法,构造方法,静态方法中都可以使用泛型,编译器也不允许创建类型变量的数组
也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用到catch字句中
private static <T extends Exception>sayHello() throws T
{
try
{
}
catch(Exxceptione)
{
throw(T)e;
}
}
7. 在泛型中也可以同时有多个参数类型,在定义他们的尖括号中用逗号隔开,例如:
publicstatic<K,V)V getValue(k key)
{
return map.get(key);
}
package com.itcast.day2;
public class Fanxin
{
public staticvoid main(String[] args)
{
add(3,5);
Numberx=add(3, 3.5);//返回Number类型
Objects=add(3, "abc");//返回Object类型
swap(new String[]{"abc","xyz", "itcast"},1,2);
//swap(new int[]{1,2,4,5}, 2, 3);//报错,泛型只能是对象型
}
static <T> T add(T x,T y)//在返回类型之前用尖括号说明
{
return null;
}
static <T> voidswap(T[] a,int i,int j)
{
T temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
自定义泛型方法的练习与类型推断总结
泛型方法练习题
1. 编写一个泛型方法,自动将Object类型对象转换成为其他类型
public staticvoid main(String[] args)
{
Objectobj="hh";
Strings=autoConvert(obj);
}
static <T> T autoConvert(Object obj)
{
return(T)obj;
}
2. 定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象
static<T> void fillArray(T[] a,T obj)
{
for(int i=0;i<a.length;i++)
{
a[i]=obj;
}
}
3. 采用自定义泛型的方法的方式打印出任意参数化类型的集合中的所有类容
在这种情况下,前面的通配符方案要比泛型方法更有效,当一个类型变量用来表达两个参数之间或者参数和返回值之间的关系时,即同一个类型变量在方法签名的两处被使用,或者类型变量在方法体代码中也被使用而不是仅在签名的时候使用,才需要泛型方法
static<T> voidprintAnyCollection(Collection<T> cols)
{
System.out.println(cols.size());
for(Objectobj:cols)
{
System.out.println(obj);
}
}
4. 定义一个方法,把任意参数类型的集合中的数据安全的复制到相应的类型的数组中
static <T> voidcopy1(Collection<T> c,T[] a)
{
inti=0;
for(Iterator<T>its=c.iterator();its.hasNext();)
{
a[i]=its.next();
i++;
}
}
5. 定义一个方法,把任意参数类型的一个数组中的数据安全的复制到相应大类型的另外一个是数组中
static <T> voidcopy2(T[] des,T[] src)
{
for(int i=0;i<src.length;i++)
{
des[i]=src[i];
}
}
自定义泛型类的应用
定义泛型的类型:
1. 如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实例类型市,这时就需要采用泛型类型的方式进行定义,也就是类级别的泛型,语法格式:
publicclass<E> genericDao
{
private T field1;
private void save(T obj)
{
}
public T getById(int id){}
}
2.类级别的泛型是根据引用该类名时同时指定的类型信息来参数化类型变量的,例如:如下两种方式都可以:
GenericDao<String> dao=null;
new genericDao<String>();
3. 注意:
在对泛型类型进行参数化时,类型参数的实例就必须是引用类型,不能是基本类型
当一个变量被声明为泛型时,只能被实例变量和发那个发调用(还有内嵌类型),而不是被静态变量和静态方法调用,因为静态变量成员是被所有参数化的类共享的,所以,静态成员不应该具有类级别的类型参数
/**
*
*/
package com.itcast.day2;
class Person//人
{
}
class Product//产品
{
}
public class GenerDao
{
/*public voidadd(Person x)
{
}
public Person findById(int id)
{
return null;
}
*/
public <T> voidadd(T x)//泛型
{
}
public <T> T findById(T id)
{
return null;
}
public static void main(String[] args)
{
GenerDao dao=newGenerDao();
dao.add(newPerson());//添加人
dao.add(newProduct());//添加产品
}
}
/*
//也可以 方法的类型和类的类型一致
* public class GenerDao<E>
{
public void add(E x)//泛型
{
}
public E findById(E id)
{
return null;
}
public static void main(String[] args)
{
GenerDao dao=newGenerDao();
dao.add(new Person());//添加人
dao.add(new Product());//添加产品
}
}
*/
通过反射获得泛型的实际类型参数
public class GeneicTest
{
public staticvoid main(String[] args) throws Exception
{
Vector<Date> v1=new Vector<Date>();
Method applyMethod=GeneicTest.class.getMethod("applyVector",Vector.class);
//得到方法
//得到参数的返回类型,type class的父类
Type[]types=applyMethod.getGenericParameterTypes();
ParameterizedType pType=(ParameterizedType)types[0];
System.out.println(pType.getRawType());//class java.util.Vector
System.out.println(pType.getActualTypeArguments()[0]);//实例化参数类型,Date
}
//得到方法接收的参数的实际类型
public staticvoid applyVector(Vector<Date>v)
{
}
}