体验泛型

泛型是提供给Javac编译器看的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带参数类型说明的集合时会去去除掉“类型”信息,使程序运行不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样由于编译生成的字节码会去掉泛型的类型信息,因此只要能跳过编译器,就可以往某个泛型集合中加入其他类型的数据。

  例如下面的代码就演示了"使用反射得到集合,然后调用add方法往原本只能存储Integer对象的集合中存储一个String类型的对象"

ArrayList<Integer> collection3 = new ArrayList<Integer>();
//对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样
System.out.println(collection3.getClass());//结果为:java.util.ArrayList
System.out.println(collection3.getClass() == collection2.getClass());//结果为true
//使用反射得到集合,然后调用add方法往原本只能存储Integer对象的集合中存储一个String类型的对象
collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
System.out.println(collection3.get(0));//输出的结果为:abc,这证明字符串对象确实是存储到了原本只能存储Integer对象的集合中
  1. 泛型是JDK1.5的所有新特性中最难深入掌握的部分,没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中,使用泛型集合,可以将一个集合中的元素限定为一个特定类型,这样集合中就只能存储同一类型的对象,这样更安全;并且当从集合中获取一个对象时,编译器也知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。
  2. 在JDK1.5之后,你还可以按原来的方式将各种不同类型的数据放到同一个集合中,但是编译时会报一个unChecked警告

泛型中的类型参数严格说明集合中装载的数据类型是什么和可以加入什么类型的数据,记住:Collection<String>和Collectin<Object>是两个没有转换关系的参数化的类型

二、了解泛型

  • ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
    • 整个称为ArrayList<E>泛型类型
    • ArrayList<E>中的E称为类型变量或类型参数
    • 整个ArrayList<Integer>称为参数化类型
    • ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
    • ArrayList<Integer>中的<>是“typeof”
    • ArrayList称为原始类型
  • 参数化类型与原始类型的兼容性:
    • 参数化类型可以引用一个原始类型的对象,编译时编译器会报警告,例如:Collection<String> c = new Vector();
    • 原始类型可以引用一个参数化类型的对象,编译时编译器会报警告,例如:Collection c = new Vector<String>();
  • 参数化类型不考虑类型参数的继承关系:
    • Vector<String> v = new Vector<Object>();//错误,语法上不通过
    • Vector<Object> v = new Vector<String>();//错误,语法上不通过

  假设Vector<String> v = new Vector<Object>;可以的话,那么以后从v中取出的对象当作String用,而v实际指向的集合中可以加入任意类型的对象,

  假设Vector< Object > v = new Vector< String >;可以的话,那么以后可以向v中加入任意类型的对象,而v实际指向的集合中只能装String类型的对象

思考:下面的代码会报错吗?(不会报错

    • Vector v1 = new Vector<String>();//参数化类型的对象可以给原始类型的引用
    • Vector<Object> v=v1;//参数化类型的引用可以指向原始类型的对象

三、自定义泛型方法

package cn.itcast.day2;
import java.io.Serializable;
/**
 * 此类是用来演示如何定义和使用泛型方法的
 * 
 * @author 孤傲苍狼
 * 
 */
public class GenericMethod {
    public static void main(String[] args) {
        add(3, 5);
        Number x1 = add(3.5, 5);// Integer类型和Double类型的交集就是Number类,Number类是这两个类的父类,所以可以定义Number类型的变量来接收返回值
        Object x2 = add(3, "abc");// Integer类型和String类型的交集就是Object类,因为Object类是所有类的父类,所以可以定义Object类型的变量来接收返回值
        /**
         * swap(new String[]{"abc","123","xdp"},1,2);的执行结果如下: 
         * abc 123 xdp 
         * abc xdp 123
         * 从结果来看,索引为1的元素和索引为2的元素的确是交换了位置
         */
        swap(new String[] { "abc", "123", "xdp" }, 1, 2);// 调用自定义泛型方法,传入的实际参数必须是引用类型的数组
        // swap(new int[]{1,2,3,4,5},1,3);//只有引用类型才能作为泛型方法的实际参数,这里的int[]数组是属于基本类型,不能作为泛型方法的参数,所以这样会报错
        printArray(new Integer[]{1,2,3});//可以传入Integer类型的数组,因为Integer类型的数组是属于引用类型的
        //printArray(new int[]{10,2,5});不能传入非引用类型的数组作为泛型方法的实际参数
    }
    /**
     * 泛型方法的定义语法: 这里定义的就是一个泛型方法 方法的返回值类型是T,即任意的类型 返回值的具体类型由传入的类型参数来定
     * 
     * @param <T>
     *            代表任意的类型
     * @param x
     * @param y
     * @return
     */
    private static <T> T add(T x, T y) {
        return null;
    }
    /**
     * 定义一个泛型方法来交换数组中指定索引位置的两个元素 这里传入的数组可以是任意类型的数组
     * 传入泛型方法的实际参数类型必须是引用类型的数组,不能是基本类型的数组
     * 
     * @param <T>
     * @param a
     * @param i
     * @param j
     */
    private static <T> void swap(T[] a, int i, int j) {
        // 数组中元素位置未交换前的打印结果
        printArray(a);
        T temp = a[i];
        a[i] = a[j];
        a[j] = temp;
        System.out.println();
        // 数组中元素位置交换后的打印结果
        printArray(a);
    }
    /**
     * 定义打印任意引用数组类型的方法
     * 
     * @param <T>
     * @param array
     */
    private static <T> void printArray(T[] array) {
        for (T t : array) {
            System.out.print(t + "	");
        }
    }
    /**
     * 定义有extends限定符,并且具有多个上边界的泛型方法,各个边界使用&符号分隔
     * @param <T>
     */
    public <T extends Serializable & Cloneable> void method(){}
}
原文地址:https://www.cnblogs.com/tanlei-sxs/p/9522435.html