[原创]Java基础巩固篇之泛型_Tomcat, WebLogic及J2EE讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Tomcat, WebLogic及J2EE讨论区 »
总帖数
4
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 2079 | 回复: 3   主题: [原创]Java基础巩固篇之泛型        下一篇 
ming.chen
注册用户
等级:中尉
经验:444
发帖:8
精华:0
注册:1970-1-1
状态:离线
发送短消息息给ming.chen 加好友    发送短消息息给ming.chen 发消息
发表于: IP:您无权察看 2016-5-12 16:47:15 | [全部帖] [楼主帖] 楼主

一. 在泛型出现之前

    JDK1.5之前的集合类中存在一个问题,我们可以向集合类中添加任意类型的对象,代码如下:

import java.util.ArrayList;
  
public class Test {
     
    public static void main(String[] args) {
         
         ArrayList collection1 = new ArrayList();
         collection1.add(1);//存储Integer对象
         collection1.add(1L);//存储Long对象
         collection1.add("xdp");//存储String对象
         /**
          * 这里会报异常: Exception in thread "main" java.lang.ClassCastException: java.lang.Long
          * at Test.main(Test.java:18)
         int i = (Integer) collection1.get(1);
     }
 }


    JDK1.5以后集合类希望你在定义集合对象的时候,明确表明你要向集合对象里添加那种类型的数据,无法加入指定类型之外的数据,例如下面的代码:

ArrayList<String> collection2 = new ArrayList<String>();
collection2.add("我是泛型集合类下的数据");
//collection2.add(1);
//报错,因为限制了collection2只能存储String类的对象,不能加入Integer类型的对象        
//collection2.add(1L);//报错,因为限制了collection2只能存储String类的对象,不能加入Long类型的对象        
//由于已经指明集合中存储的是字符串类型的对象,因此这里不用再强制转型了
String element = collection2.get(0);

    泛型是提供给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警告。


二. 了解泛型

    ·ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下的术语:

    (1)整个ArrayList<E>称为泛型类型

   (2)ArrayList<E>中的E为类型变量或类型参数

    (3)整个Arraylist<Integer>称为参数化类型

    (4)ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数

    (5)ArrayList<Integer>中的<>是“typeof”

    (6)ArrayList称为原始类型

    ·参数化类型与原始类型的兼容性:

        参数化类型可以引用一个原始类型的对象,编译时编译器会报警报,例如:Collection<String> c=new Vector();

        原始类型可以引用一个参数化类型的对象,编译时编译器会报警报,例如:Collection c=new Vector<String>();

    ·参数化类型不考虑类型参数的继承关系:

        Vector<String> v =new Vector<Object>()//错误,语法不通

        Vector<Object> v =new Vector<String>()//错误,语法不通

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

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

        Vector<Object> v =v1;参数化类型的引用可以指向原始类型的对象


三. 泛型中的?通配符

    如果要你定义一个方法,这个方法可以打印出任意参数化类型的集合中的所有数据,这个方法应该怎么定义呢?

    错误的定义:

public static void printAllCollection(Collection<Object> collection){
    for(Object obj:collection){
        System.out.println(obj);
    }
    colletion.add("abc");//这步没错
    collection = new HashSet<Date>{};//会报错误
}

    正确的定义:

public static void printAllCollection(Collection<?> collection){
    for(Object obj:collection){
        System.out.println(obj);
    }
    //collection.add("abc");//报错,因为collection不知道未来匹配的一定是String类型
    collection= new HashSet<Date>();//不会报错
}

总结:使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数无关的方法,不能调用与参数有关的方法。


四. 自定义泛型方法

import java.io.Serializable;
/**
 * 此类是用来演示如何定义和使用泛型方法的
 * 
 */
public class Test {
    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 + "\t");
        }
    }
    /**
     * 定义有extends限定符,并且具有多个上边界的泛型方法,各个边界使用&符号分隔
     * @param <T>
     */
    public <T extends Serializable & Cloneable> void method(){}
}

总结普通方法、构造方法和静态方法都可以使用泛型。


五. 自定义泛型类

/**
 * 自定义泛型类
 *
 */

public class GenericArrayList<E> {
 
  Object[] objects=new Object[10];
  int index=0;

  /**

  * 父类引用指向子类对象

  * @param o

  */
  public void add(E o){
   if(index==objects.length){
      Object[] newObjects=new Object[objects.length*2];
      System.arraycopy(objects, 0, newObjects, 0, objects.length);
      objects=newObjects;
    }
     objects[index]=o;
     index++;
  }


    /**

    * 获取数组的长度

    * @return

    */

    public int size(){
       return index;
    }
    
   public static void main(String[] args) {
       //把E替换成你想要实现的类或类型
        GenericArrayList<String> geneArray=new GenericArrayList<String>();
        geneArray.add("a");
        System.out.println(geneArray.size());
    }
}

总结:1.在对泛型类进行参数化时,类型参数的实例必须是引用类型,不能是基本数据类型

          2.当一个变量声明为泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法调用,因为静态成员是被所有参数化的类所共享的,所以静态成员不应该拥有类级别的类型参数。





    



该贴被ming.chen编辑于2016-5-12 16:56:16



赞(0)    操作        顶端 
filogra
注册用户
等级:少校
经验:1408
发帖:13
精华:0
注册:2015-6-2
状态:离线
发送短消息息给filogra 加好友    发送短消息息给filogra 发消息
发表于: IP:您无权察看 2016-5-16 7:23:26 | [全部帖] [楼主帖] 2  楼

受益匪浅,感谢分享~



赞(0)    操作        顶端 
duff
注册用户
等级:少校
经验:968
发帖:0
精华:0
注册:2015-7-22
状态:离线
发送短消息息给duff 加好友    发送短消息息给duff 发消息
发表于: IP:您无权察看 2016-5-16 7:44:24 | [全部帖] [楼主帖] 3  楼

学习了



赞(0)    操作        顶端 
beefly
注册用户
等级:上尉
经验:758
发帖:1
精华:0
注册:2015-7-27
状态:离线
发送短消息息给beefly 加好友    发送短消息息给beefly 发消息
发表于: IP:您无权察看 2016-5-16 20:11:16 | [全部帖] [楼主帖] 4  楼



赞(0)    操作        顶端 
总帖数
4
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论