java 泛型使用详解

1、概述

泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。在使用的时候,具体指定操作数据类型,这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。

2、泛型举例

例子1:

//泛型例子
public static void testGenerics(){

        ArrayList list = new ArrayList();
        list.add("nihao");
        list.add(100);

        for(int i =0;i<list.size();i++){
            String content = (String)list.get(i);
            System.out.println(content);
        }

    }

上述代码编译可以正常通过,但运行会报一下错误:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
说明:
因为ArrayList内部存储数据用的是Object数组,所以可以向ArrayList添加任意类型数据(String,Integer),所以在编译期间可以正常通过。但是再运行期间,由于添加了Integer数据,强制转换成String使用的时候就会报错。为了在编译期间(编译器做出错误提示)就可以解决此类问题,泛型便出现了。
我们将上述ArrayList 的声明初始化代码修改如下,编译器会在编译阶段就会发现类似这样的问题。

这里写图片描述
如上图,可以发现,eclipse做出了错误提醒。
例子2:

public static void testRunGenerics(){
        List<String> list1 = new ArrayList<String>();
        List<Long> list2 = new ArrayList<Long>();

    System.out.println(list1.getClass().equals(list2.getClass()));

    }

运行结果:true
上述例子说明,java代码在编译后,会去除泛型信息。也即是说,java中泛型只是在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型相关信息擦除,并在对象进入和离开方法的边界处,添加类型检查,和类型转换的方法。
一句话:泛型类型,只在编译阶段提供,类型信息,用于检查类型安全,和生成类型转换方法。编译成的class文件,其实都是相同的基本类型。

3、泛型的定义和使用

泛型有三种使用方式,泛型接口,泛型类,泛型方法;

泛型接口

Show.java文件

/**
 * 
 */
package generics;

/**
 * @author chzhao
 *
 */
public interface Show <T,M>{

    public void show(T t,M m);

}

TestGeneric.java文件

package generics;

import java.util.Date;


//在类的定义的时候指定第一个参数为String,另一个参数不指定任何类型
class ShowTest <M> implements Show<String,M>{

    @Override
    public void show(String t, M m) {
        // TODO Auto-generated method stub
        System.out.println(t);
        System.out.println(m);
    }

}

public class TestGeneric {

    public static void main(String[] args) {
        //可以在使用的时候指定第二个参数类型。
        //注意接口Show中指定参数,第一个参数必须和
        //ShowTest定义指定的第一个参数类型相同,如不同编译器报错。
        Show <String,Date>showTest = new ShowTest<Date>();
        Date date = new Date();
        showTest.show("nihao", date);


    }
}

运行结果:
nihao
Sat Feb 24 15:01:17 CST 2018

泛型类

在类的定义中使用泛型,我们称这样的类为泛型类。jdk中最典型的的泛型类,如List Set Map。
泛型类的基本写法:

class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{
  private 泛型标识 /*(成员变量类型)*/ var; 
  .....

  }
}

TestGeneric.java文件

package generics;

import java.util.Date;


//在实例化泛型类时,必须指定V的具体类型
//此处V可以写成任意符合java规则的标识
class GenericValue <V>{
     //value这个成员变量的类型为V,V的类型由外部指定  
    private V value;
    //泛型构造方法,形参v的类型由该类具体使用时指定。
    public GenericValue(V v){
        this.value = v;
    }
    //泛型方法 返回值由该类具体使用时指定。
    public V getValue(){

        return  value;
    }

}

public class TestGeneric {

    public static void main(String[] args) {

        GenericValue<String> genericValue = new GenericValue<String>("weiwei");
        System.out.println(genericValue.getValue());
        GenericValue<Date> genericValue1 = new GenericValue<Date>(new Date());
        System.out.println(genericValue1.getValue());

    }
}

运行结果:
weiwei
Sat Feb 24 15:26:25 CST 2018

泛型类使用,记住一点在使用的时候(声明和实例化的时候)具体指定参数类型。
还有一点说明,定义了一个泛型类,我们在使用的时候指定具体类型,主要作用给编译器在编译java源代码的时候使用,可以起到类型安全检查,和类型强制转换代码的生成。但记住我们在使用的时候,也可以不指定具体类型(其实也就是放弃了使用泛型,也就享受不到泛型带来的好处了),这时候,代码也是可以编译通过运行的,如下面例子。

public class TestGeneric {

    public static void main(String[] args) {

        GenericValue genericValue = new GenericValue("weiwei");
        System.out.println(genericValue.getValue());
        GenericValue genericValue1 = new GenericValue(new Date());
        System.out.println(genericValue1.getValue());

    }
}

运行结果:
weiwei
Sat Feb 24 15:26:25 CST 2018

泛型方法

    /**泛型方法
     * @param T 声明一个泛型类型
     * @param c 用来创建泛型对象
     * @return T 返回一个泛型类型的返回值
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
     //<T> 此作用说明该方法为泛型方法
     //参数(Class<T>)指定此方法的具体泛型类型
    public <T> T getObject(Class<T> c) throws InstantiationException, IllegalAccessException{ 
        T t = c.newInstance();
        return t;
    }

测试代码TestGeneric.java文件

package generics;

import java.util.Date;


class Peoples{
    private String name = "张三";

    public void showName(){

        System.out.println(name);
    }

}

//在实例化泛型类时,必须指定V的具体类型
//此处V可以写成任意符合java规则的标识
class GenericValue <V>{
     //value这个成员变量的类型为V,V的类型由外部指定  
    private V value;
    //泛型构造方法,形参v的类型由该类具体使用时指定。
    public GenericValue(V v){
        this.value = v;
    }
    //泛型方法 返回值由该类具体使用时指定。
    public V getValue(){

        return  value;
    }


    /**泛型方法
     * @param T 声明一个泛型类型
     * @param c 用来创建泛型对象
     * @return T 返回一个泛型类型的返回值
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public <T> T getObject(Class<T> c) throws InstantiationException, IllegalAccessException{ 
        T t = c.newInstance();
        return t;
    }

}



public class TestGeneric {

    public static void main(String[] args) {

        GenericValue <String>genericValue = new GenericValue<String>("weiwei");
        try {
            Peoples people = genericValue.getObject(Peoples.class);
            people.showName();

        } catch (InstantiationException | IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }
}

运行结果:张三

静态泛型方法

import java.util.ArrayList;
import java.util.List;

public class TestGeneric {

    //打印容器泛型方法
    public static <T> void printList(List<T> t){

        for(T tt:t){
            System.out.print(tt+"\t");
        }
        System.out.println();

    }


    public static void main(String[] args) {

        List<Integer> intList = new ArrayList<Integer>();
        List<String> stringList = new ArrayList<String>();
        for(int i =0;i<10;i++){
            intList.add(i);
            stringList.add("weiwei"+i);

        }
        //打印list容器
        printList(intList);
        printList(stringList);



    }
}

运行结果:
这里写图片描述

泛型方法能使方法独立于类而产生变化,对于泛型方法的使用有一个指导原则:如果你能做到,你就该尽量使用泛型方法。也就是说,如果使用泛型方法将整个类泛型化,那么就应该使用泛型方法。对于一个static的方法,无法访问泛型类型的参数。所以如果static方法要使用泛型能力,就必须使其成为泛型方法

希望对您有所帮助!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值