这是我关于泛型的学习日记中的部分内容,希望对您有帮助。
泛型是提供给编译器使用的,可以限定集合中的输入类型让编译器挡住源程序中的非法输入,这样可以让程序更安全。另外,当从集合中获取一个对象时,
编译器也可以知道这个对象的类型,不需要再进行强制类型转换,使得代码编写更方便。编译器编译带类型说明的集合时会去掉类型信息,是程序运行效率不受影响。
- <P> </P>
- <P>//定义一个方法,这个方法可以打印出任意参数类型的集合中的所有数据。
- //类型通配符“?”:使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用类型参数,可以调用与参数无关的方法,但不可调用与参数类型有关的方法。
- public static void printCollection(Collection<?> collection){
- //collection.add(1);编译错误,collection可以作为一个引用变量被引用,但是不可以用它来调用一个与参数类型有关的方法,如add。
- System.out.println(collection.size());//因为size方法是一个与类型无关的方法,所以可以被collection调用。
- for(Object obj : collection){
- System.out.println(obj);
- }
- }
- //在此方法中如果将参数改为Collection<Object> collection,当吧collection3(在main方法中)传递进来时就会报错,因为collection3中元素是Integer类型的,这样就相当于Collection<Object> collection=ArrayList<Integer> collection3
- </P>
- <P>//用泛型方法打印出任意参数化类型的集合中的所有内容
- public static <T> void printCollection2(Collection<T> collection){
- //collection.add(1);
- System.out.println(collection.size());
- for(Object obj : collection){
- System.out.println(obj);
- }
- }
- //利用泛型方法将任意参数类型的集合中的数据安全的复制到相应类型的数组中。
- public static <T> void copy1(Collection<T> dest,T[] src){
- }
- public static <T> void copy2(T[] dest,T[] src){
- }
- //如果不用泛型方法,即代码为statif void copy(Collection dest, Object[] b){];就可能会出现A类型数据被复制到B类型的数组中,出现不期望的结果。
- //对异常使用泛型(分成设计中常用)
- private static <T extends Exception> sayHi() throws T{
- try{
- System.out.println("Hi, there!");
- }
- catch(Exception e){ //catch参数不可以为T,必须指出catch的异常的类型或者直接catch Exception。
- throw (T)e; //抛出异常时可以为T,表示将catch到的e封装成T类型的异常。
- }
- }
- /*用类型变量表示异常称为参数化的异常,可以用于方法的throws列表中,但不可以用作catch语句中。*/
- public static void main(String[] args) throws Exception {
- ArrayList collection1 = new ArrayList();
- collection1.add(1);
- collection1.add(1L);
- collection1.add("abc");
- //int i = (Integer)collection1.get(1);此处取出的1并不是int类型,运行结果是:throwsjava.lang.classCastException:java.lang.Long cannot be cast to java.lang.Int。
- //集合中应用泛型
- ArrayList<String> collection2 = new ArrayList<String>();//<String>即指定了集合中的元素类型为String类型。
- //collection2.add(1); 会报错
- //collection2.add(1L); 会报错
- collection2.add("abc");
- String element = collection2.get(0);//此处不需要再进行类型转换
- //反射中应用泛型
- //new String(new StringBuffer("abc"));
- Constructor<String> constructor1 = String.class.getConstructor(StringBuffer.class); //<String>代表String类型的构造方法。
- String str2 = constructor1.newInstance(new StringBuffer("abc"));
- System.out.println(str2.charAt(2));
- ArrayList<Integer> collection3 = new ArrayList<Integer>();
- System.out.println(collection3.getClass() == collection2.getClass());
- //用反射方法越过编译器,向一个Integer类型的ArrayList中加入字符串。
- collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
- /*上面的代码没有问题,是因为泛型是给编译器看的,当编译器编译结束,就会去掉collection3中的Integer的信息,同理也会去掉collection2中的String信息。
- 这时collection2和collection3的类型还是一样的,这就是去类型化。*/
- System.out.println(collection3.get(0));
- printCollection(collection3);
- //类型通配符“?”
- Class<?> y;
- Class<String> x ;
- //如果让y=x,不报错,而让x=y则报错。因为y返回的是任意类型,而x返回的是Sring类型的Class,我们可以将一个具体的类型赋给问号代表的任意类型,而不可以将一个任意类型赋给一个具体的类型。
- //泛型集合的综合应用
- HashMap<String,Integer> maps = new HashMap<String, Integer>();
- maps.put("zxx", 28);
- maps.put("lhm", 35);
- maps.put("flx", 33);
- Set<Map.Entry<String,Integer>> entrySet = maps.entrySet();
- for(Map.Entry<String, Integer> entry : entrySet){
- System.out.println(entry.getKey() + ":" + entry.getValue());
- }
- //在此部分代码中,不可以对Map直接进行迭代,因为它没有实现Iterable接口,所以要先将Map转化成一个Set(实现了Iterable接口),它的每个元素是Entry,然后利用Set对元素进行迭代。
- //类型推断问题
- add(3,5);//右边两个参数的类型取交集,为Integer
- //此处调用add方法时,3和5不再是int类型,而是被自动装箱为Integer。
- Number x1 = add(3.5,3);//右边两个参数的类型取交集,为Number
- Object x2 = add(3,"abc");//右边两个参数的类型取交集,为Object
- swap(new String[]{"abc","xyz","itcast"},1,2);
- //swap(new int[]{1,3,5,4,5},3,4);报错,int数组中的每个元素不会被装箱为Integer对象,因为int数组本身就是对象了。
- //泛型的实际类型只能是引用类型而不可以是基本类型。T不可以被int替换(swap方法返回值类型也是T)。
- Object obj = "abc";
- String x3 = autoConvert(obj);
- copy1(new Vector<String>(),new String[10]);//没问题,前后类型一致
- copy2(new Date[10],new String[10]);//没问题,因为编译器会首先进行类型推断,去Date和String类型的交集,然后将二者都作为Object看待。
- //copy1(new Vector<Date>(),new String[10]);报错,因为为Vector指定<Date>说明类型参数为Date类型,而后面的类型为String,二者不搭配。
- //用反射获得泛型的实际类型参数。应用与框架和webservice等。
- Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
- Type[] types = applyMethod.getGenericParameterTypes();
- ParameterizedType pType = (ParameterizedType)types[0];
- System.out.println(pType.getRawType());//Vector
- System.out.println(pType.getActualTypeArguments()[0]);//Date
- }
- }</P>
复制代码
补充基本知识点:
1,参数化类型与原始类型的兼容性。
参数化类型可以引用一个原始类型的对象,但编译器会发出警告。如Collection<String> c=new Vector();
原始类型可以引用一个参数化类型的对象。编译器也会发出警告。如Collection c=new Vector<String>;
取决于编译器。
问题:
下面代码编译时会报错吗:
Vector v1=new Vector<String>();
Vector<Object> v=v1;
不会报编译错误。因为编译器是一行行的检查语法的,第一句中将一个参数化的类型赋给了一个原始类型是可以的,第二句中将一个原始类型赋给一个参数化类型,也是可以,在编译阶段上下两句并没有关系,不会报错。当然在运行时是会报错的。
2,参数化类型不考虑类型参数的继承关系。
例如,Collection<String>和Collection<Object>是两个没有转换关系的参数化类型。
Vector<String> v=new Vector<Object>();错误
Vector<Object> v=new Vector<String>();错误
3,创建数组实例时,数组元素不可以使用参数化的类型。
Vector<Integer> v[]=new Vector<Integer>[10]();错误!
4,类型参数的类型推断。
编译器判断泛型方法的实际类型参数的过程称为类型推断,前面main方法中有举例。根据调用泛型方法时实际传递的参数类型或返回值类型推断
5,应用于类的泛型:将泛型加载类身上
如果类的实例对象中多处需要用到的泛型参数的类型要保持同一种实际类型,就要使用类级别的泛型类型定义方式。类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,注意类型参数的实例必须为应用类型的。
将泛型加载类身上,可以保确保类中方法应用的类型与类是一致的。
eg,public class GenericUsage<E>{
public void method1(E x){}
public E method2(int y){}
public void method3(E obj){}
public void method3(int z){}//方法的重载
public static <E> void method4(E obj){}
}
如果在类名后没有加<E>,例如method1和method2这两个方法中的E不会相互制约,两者可能是不同的类型,编译时不会报错,但运行时会报错。
问题:类中只有一个方法需要使用泛型,要使用类级别的泛型还是方法基本的泛型:
类级别。
6,静态方法(static)中的泛型:
在前例中的method4是一个静态方法,如果其返回值前没有<E>,会报错,因为静态方法是不用创建对象就可以被调用的,一般不使用泛型类型。如果的确需要使用,则需要定义自己的泛型<E>。这里静态方法中的E和类中的E没有关系。当一个变量被申明为泛型时,只可以被实例变量和方法调用,不可以被static变量或方法调用。static成员不应该有类级别的参数。
|