------- android培训、java培训、期待与您交流! ----------
泛型是jdk1.5的新特性。
没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全;并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。
package cn.itcast.day2;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
public class GenericTest {
public static void main(String[] args) throws Exception {
//不加泛型容易导致 类型转换出错!
ArrayList collection1 = new ArrayList();
collection1.add(1);
collection1.add(1L);
collection1.add("abc");
//int x = (Integer)collection1.get(1);//这个集合的1角标位置是Long型的1L,转换为Integer时类型转换出错!
//System.out.println(x);
//加泛型后,只能添加<>里面指定的类型的元素
ArrayList collection2 = new ArrayList();
collection2.add("abc");
String str = collection2.get(0);//获取元素的时候,不需要类型强制转换。
System.out.println(str);//abc
//new String(new StringBuffer("hello"));要用反射来实现这句话
//先得到String(StringBuffer buffer) 构造函数
//加泛型前:
Constructor strConstructor1 = String.class.getConstructor(StringBuffer.class);
String str2 = (String)strConstructor1.newInstance(new StringBuffer("hello"));//需要类型转换
System.out.println(str2.charAt(1));//e
//加泛型后:
Constructor strConstructor2 = String.class.getConstructor(StringBuffer.class);
String str3 = strConstructor2.newInstance(new StringBuffer("hello"));//不需要类型转换
System.out.println(str3.charAt(1));//e
}
}
Constructor is a raw type. References to generic type Constructor
泛型是给编译器看的。编译完成后,编译器会把泛型给去掉。这时 ArrayList
泛型是给编译器看的,为什么通过反射可以越过它?
我直接给collection3里添加字符串,编译器是会报错的。透过编译器,进行反射可以通过。
package cn.itcast.day2;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
public class GenericTest {
public static void main(String[] args) throws Exception {
//不加泛型容易导致 类型转换出错!
ArrayList collection1 = new ArrayList();
collection1.add(1);
collection1.add(1L);
collection1.add("abc");
//int x = (Integer)collection1.get(1);//这个集合的1角标位置是Long型的1L,转换为Integer时类型转换出错!
//System.out.println(x);
//加泛型后,只能添加<>里面指定的类型的元素
ArrayList collection2 = new ArrayList();
collection2.add("abc");
String str = collection2.get(0);//获取元素的时候,不需要类型强制转换。
System.out.println(str);//abc
//new String(new StringBuffer("hello"));要用反射来实现这句话
//先得到String(StringBuffer buffer) 构造函数
//加泛型前:
Constructor strConstructor1 = String.class.getConstructor(StringBuffer.class);
String str2 = (String)strConstructor1.newInstance(new StringBuffer("hello"));//需要类型转换
System.out.println(str2.charAt(1));//e
//加泛型后:
Constructor strConstructor2 = String.class.getConstructor(StringBuffer.class);
String str3 = strConstructor2.newInstance(new StringBuffer("hello"));//不需要类型转换
System.out.println(str3.charAt(1));//e
//编译完成后,编译器会把泛型给去掉。这时两个ArrayList对象的类型是一致的,也可以说是同一份字节码。
ArrayList collection3 = new ArrayList();
System.out.println(collection3.getClass()==collection2.getClass());//true
//泛型是给编译器看的,为什么通过反射可以越过它?
//我直接给collection3里添加字符串,编译器是会报错的:
//collection3.add("abc");
//透过编译器,进行反射:
collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");//这个方法属于类的字节码身上的,与对象无关。得到这个方法以后,我再拿着这个方法去作用于某个对象。
System.out.println(collection3.get(0));//abc。这时编译器提醒我打印出来的是Integer,可是运行的时候就不会去检查,你装进去什么,就把什么打出来。
}
}
ArrayList
Vector
Vector v1 = new Vector
Vector
都不会报错的啦!
泛型中的?通配符:
> 代表任意类型。
public static void printCollection(Collection
for(Object obj:cols) {
System.out.println(obj);
}
/* cols.add("string");//没错
cols = new HashSet
}
public static void printCollection(Collection> cols) {
for(Object obj:cols) {
System.out.println(obj);
}
//cols.add("string");//错误,因为它不知自己未来匹配就一定是String
cols.size();//没错,此方法与类型参数没有关系
cols = new HashSet
}
泛型中的?通配符的扩展:
Vector extends Number> y =new Vector
Vector
上面的代码错误,原理与Vector
只能通过强制类型转换方式来赋值。
HashMap<String,Integer>hm = newHashMap<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());
}
package cn.itcast.day2;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class GenericTest {
public static void main(String[] args) throws Exception {
//不加泛型容易导致 类型转换出错!
ArrayList collection1 = new ArrayList();
collection1.add(1);
collection1.add(1L);
collection1.add("abc");
//int x = (Integer)collection1.get(1);//这个集合的1角标位置是Long型的1L,转换为Integer时类型转换出错!
//System.out.println(x);
//加泛型后,只能添加<>里面指定的类型的元素
ArrayList collection2 = new ArrayList();
collection2.add("abc");
String str = collection2.get(0);//获取元素的时候,不需要类型强制转换。
System.out.println(str);//abc
//new String(new StringBuffer("hello"));要用反射来实现这句话
//先得到String(StringBuffer buffer) 构造函数
//加泛型前:
Constructor strConstructor1 = String.class.getConstructor(StringBuffer.class);
String str2 = (String)strConstructor1.newInstance(new StringBuffer("hello"));//需要类型转换
System.out.println(str2.charAt(1));//e
//加泛型后:
Constructor strConstructor2 = String.class.getConstructor(StringBuffer.class);
String str3 = strConstructor2.newInstance(new StringBuffer("hello"));//不需要类型转换
System.out.println(str3.charAt(1));//e
//编译完成后,编译器会把泛型给去掉。这时两个ArrayList对象的类型是一致的,也可以说是同一份字节码。
ArrayList collection3 = new ArrayList();
System.out.println(collection3.getClass()==collection2.getClass());//true
//泛型是给编译器看的,为什么通过反射可以越过它?
//我直接给collection3里添加字符串,编译器是会报错的:
//collection3.add("abc");
//透过编译器,进行反射:
collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");//这个方法属于类的字节码身上的,与对象无关。得到这个方法以后,我再拿着这个方法去作用于某个对象。
System.out.println(collection3.get(0));//abc。这时编译器提醒我打印出来的是Integer,可是运行的时候就不会去检查,你装进去什么,就把什么打出来。
printCollection(collection3);
//泛型集合类:
HashMap hm = new HashMap();
hm.put("zxx", 28);
hm.put("lhm", 35);
hm.put("flx", 33);
Set< Map.Entry > entrySet = hm.entrySet();
for(Map.Entry entry : entrySet)
{
System.out.println( entry.getKey() + ":" + entry.getValue() );
}
}
private static void printCollection(Collection> coll) {
for(Object obj : coll)
{
System.out.println(obj);
}
//coll.add("abc");//错误。因为它不知未来匹配就一定是String。
coll.size();
coll = new HashSet();//HashSet是Collection的子类。Date是一个表示时间的类。
}
}
Entry是Map的内部类:
对在jsp页面中也经常要对Set或Map集合进行迭代:
${entry.key}:${entry.value}
由C++的模板函数引入自定义泛型
return x+y;
}
return x+y;
}
return x+y;
}
template
T add(T x,T y) {
return (T) (x+y);
}
Java中的泛型类型(或者泛型)类似于C++中的模板。但是这种相似性仅限于表面,Java语言中的泛型基本上完全是在编译器中实现,用于编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为擦除(erasure)(编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)。这是因为扩展虚拟机指令集来支持泛型被认为是无法接受的,这会为Java厂商升级其JVM造成难以逾越的障碍。
package cn.itcast.day2;
public class GenericTest {
public static void main(String[] args) {
//类型的推断:取最大公约数、或者取交集。
Integer in = add(3 , 5);
Number x1 = add(3.5 , 3);
Object obj = add(3 , "abc");
}
//要定义一个类型必须在返回值之前
private static T add(T x, T y) {
//return x + y; //The operator + is undefined for the argument type(s) T, T
return null;
}
}
泛型的实际类型只能是对象那种类型,不能是基本数据类型:
package cn.itcast.day2;
public class GenericTest {
public static void main(String[] args) {
//类型的推断:取最大公约数、或者取交集。
Integer in = add(3 , 5);
Number x1 = add(3.5 , 3);
Object obj = add(3 , "abc");
swap(new String[]{"abc","abc1","abc2"}, 1, 2);
//swap(new int[]{1,2,3,4,5}, 3, 5);//报错。泛型的实际类型只能是对象那种类型,不能是基本数据类型。
}
//交换数组中的两个元素的位置的泛型方法
public static void swap(T[] arr, int x, int y) {
T temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
//要定义一个类型必须在返回值之前
private static T add(T x, T y) {
//return x + y; //The operator + is undefined for the argument type(s) T, T
return null;
}
}
只有引用类型才能作为泛型方法的实际参数,
对于add方法,使用基本类型的数据进行测试没有问题,这是因为自动装箱和拆箱了。
swap(new int[3],3.5);语句会报告编译错误,这是因为编译器不会对new int[3]中的int自动拆箱和装箱了,因为new int[3]本身已经是对象了,你想要的有可能就是int数组呢?它装箱岂不弄巧成拙了。
除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,
例如,Class.getAnnotation()方法的定义。
public A getAnnotation(Class annotationClass)
并且可以用 & 来指定多个边界,如
普通方法、构造方法和静态方法中都可以使用泛型。
Vector
用下面的代码说明对异常如何采用泛型:
private static
try{
}catch(Exception e){
throw (T)e;
}
}
public static
编写一个泛型方法,自动将Object类型的对象转换成其他类型。
package cn.itcast.day2;
public class GenericTest {
public static void main(String[] args) throws Exception {
Object obj123 = "abc";
String convert = autoConvert(obj123);
}
//编写一个泛型方法,自动将Object类型的对象转换成其他类型。
private static T autoConvert(Object obj) {
return (T)obj;
}
}
定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象。
package cn.itcast.day2;
public class GenericTest {
public static void main(String[] args) throws Exception {
String[] arr = new String[3];
fillArray(arr,"abc");
}
//定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象。
private static void fillArray(T[] arr, T obj) {
for(int x=0; x
package cn.itcast.day2;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
public class GenericTest {
public static void main(String[] args) throws Exception {
ArrayList collection3 = new ArrayList();
//透过编译器,进行反射:
collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
System.out.println(collection3.get(0));
printCollection(collection3);
}
//采用自定泛型方法的方式打印出任意参数化类型的集合中的所有内容。
public static void printCollection(Collection coll, T obj1) {
for(T obj : coll)
{
System.out.println(obj);
}
coll.add(obj1);
}
private static void printCollection(Collection> coll) {
for(Object obj : coll)
{
System.out.println(obj);
}
//coll.add("abc");//错误。因为它不知未来匹配就一定是String。
coll.size();
coll = new HashSet();//HashSet是Collection的子类。Date是一个表示时间的类。
}
}
Ø
在这种情况下,前面的通配符方案要比范型方法更有效,当一个类型变量用来表达两个参数之间或者参数和返回值之间的关系时,即同一个类型变量在方法签名的两处被使用,或者类型变量在方法体代码中也被使用而不是仅在签名的时候使用,才需要使用范型方法。
package cn.itcast.day2;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
public class GenericTest {
public static void main(String[] args) throws Exception {
ArrayList al = new ArrayList();
al.add(168);
al.add(18);
Integer[] intArr = new Integer[al.size()];
copy(al, intArr);
Arrays.sort(intArr);
for(Integer inte : intArr)
{
System.out.println(inte);
}
}
//定义一个方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中。
private static void copy(Collection coll, T[] arr) {
Iterator it = coll.iterator();
for(int x=0; it.hasNext(); x++)
{
arr[x] = it.next();
}
}
}
package cn.itcast.day2;
import java.util.Collection;
import java.util.Date;
import java.util.Vector;
public class GenericTest {
public static void main(String[] args) throws Exception {
copy(new Vector(), new String[10]);
copy1(new Date[10], new String[10]);//取两个的最小公倍数,都被认为是Object[]。
//copy(new Vector(), new String[10]);//报错,泛型的类型推断是由Vector的泛型传进来的。类型推断具有传播性。
}
//定义一个方法,把任意参数类型的一个数组中的数据安全地复制到相应类型的另一个数组中。
private static void copy1(T[] dest, T[] src) {
for(int x=0; xvoid copy(Collection coll, T[] arr) {
Iterator it = coll.iterator();
for(int x=0; it.hasNext(); x++)
{
arr[x] = it.next();
}
}
}
swap(new String[3],3,4) à static
add(3,5) à static
fill(new Integer[3],3.5f) à static
int x =(3,3.5f) à static
copy(new Integer[5],new String[5]) àstatic
copy(new Vector
如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型,语法格式如下:
public class GenericDao
private T field1;
public void save(T obj){}
public T getById(int id){}
}
package cn.itcast.day2;
import java.util.Set;
//Dao: Data Access Object(数据访问对象)--->crud(增删改查)
//为了制约这个类的方法操作的都是同一种对象,我们把泛型定义到了类上。
//我们不希望存进去的是猪,取出来的是狗。
public class GenericDao {
public void add(T p) {
}
public T findByID(int id) {
return null;
}
public void delete(T obj) {
}
public void delete(int id) {
}
public void update(T obj) {
}
//类里面的静态方法是不可以用类上面定义的泛型的,类上的泛型是约束操作的对象的,
//静态方法你连对象都不用就可以直接调用,所以还不知道你操作的是什么对象了。
//public static void update2(T obj) {} //错误,静态方法不能使用类上定义的泛型。
//可以先提示您输入的用户名存不存在,再提示您输入的密码正不正确。
public T findByUserName(String userName) {
return null;
}
//可以提示您输入的用户名或密码错误!
public T findByUserName(String userName, String password) {
return null;
}
public Set findByConditions(String where) {
return null;
}
}
package cn.itcast.day2;
import cn.itcast.day1.ReflectPoint;
public class GenericTest {
public static void main(String[] args) throws Exception {
GenericDao dao = new GenericDao();
dao.add(new ReflectPoint(3,3));
}
}
package cn.itcast.day1;
import java.util.Date;
public class ReflectPoint {
private Date birthday = new Date();
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
private int x;
public int y;
private String str1;
public String str2;
public String str3;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
public ReflectPoint(int x, int y, String str1, String str2, String str3) {
super();
this.x = x;
this.y = y;
this.str1 = str1;
this.str2 = str2;
this.str3 = str3;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public String toString() {
return x + "::" + y + "::" + str1 + "::" + str2 + "::" + str3;
}
}
示例代码:
Class GenericalReflection {
private Vector
public void setDates(Vector
this.dates = dates;
}
public static void main(String[] args) {
Method methodApply = GenericalReflection.class.getDeclaredMethod("applyGeneric", Vector.class);
ParameterizedType pType = (ParameterizedType) (methodApply .getGenericParameterTypes())[0];
System.out.println("setDates("
+ ((Class) pType.getRawType()).getName() + "<"
+ ((Class) (pType.getActualTypeArguments()[0])).getName()
+ ">)" );
}
}
protected Class
public DaoBaseImpl() {
Type type = this.getClass().getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) type;
this.clazz = (Class) pt.getActualTypeArguments()[0];
System.out.println("clazz = " + this.clazz);
}
}
}
Method类中的方法:
Type[] |
getGenericParameterTypes() 按照声明顺序返回 Type 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型的。 |
Type类的子类:
java.lang.reflect 接口 ParameterizedType
表示参数化类型。
方法摘要 | |
---|---|
Type[] |
getActualTypeArguments() 返回表示此类型实际类型参数的 Type 对象的数组。 |
Type |
getOwnerType() 返回 Type 对象,表示此类型是其成员之一的类型。 |
Type |
getRawType() 返回 Type 对象,表示声明此类型的类或接口。 |
package cn.itcast.day2;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Vector;
import cn.itcast.day1.ReflectPoint;
public class GenericTest {
public static void main(String[] args) throws Exception {
//我们用反射来得到这个集合中存放的对象的类型。
//以后学webService的时候,有个方法返回一个集合,如果不知道这个集合里面元素的类型,那么就不知道要把那么数据转化成什么对象。
//Vector v1 = new Vector();
Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);//反射里方法的参数是没有泛型的。
Type[] types = applyMethod.getGenericParameterTypes();
/*Type下面有各种子类:Class是其中一个,
所有已知子接口: GenericArrayType, ParameterizedType(参数化类型), TypeVariable, WildcardType
所有已知实现类: Class */
ParameterizedType pType = (ParameterizedType)types[0];//types[0]是applyMethod方法中的第一个参数。
System.out.println( pType.getRawType() );//class java.util.Vector
System.out.println( pType.getActualTypeArguments()[0] );//class java.util.Date
}
//我没法通过v1来知道前面这个变量的类型,但是我可以通过这个方法来知道它的参数列表的类型。
//因为这个Method类提供了相应的方法:
//Type getGenericReturnType()、获得这个方法的返回值类型
//Type[] getGenericParameterTypes()、 获得这个方法的 以泛型那种类型的参数列表。
//Class>[] getParameterTypes()、获得这个方法的参数列表
public static void applyVector(Vector v1) {
}
//因为你在这里写一个参数:泛型是Integer的Vector,就已经报错了,编译器认为他们是同一个方法。
/*public static void applyVector(Vector v1) {
}*/
}
------- Windows Phone 7手机开发、.Net培训、期待与您交流! -------