泛型

来源:互联网 发布:mac zip 打不开 编辑:程序博客网 时间:2024/06/07 14:12


一:概述

/**
     * 在jdk1.5之前,我们在往一个集合中存放一个对象或者基本数据类型的时候,取出来的时候,如果数据类型
     * 不匹配,那么就不可避免的需要做一些强制类型转换的工作,那么这就不可避免的出现类型转换异常。
     */


二:Demo1

<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;import java.util.ArrayList;import java.util.List;/** * 为何我们需要使用泛型? * User: OF895 * Date: 14-7-21 * Time: 下午9:51 */public class Demo1 {    /**     * 在jdk1.5之前,我们在往一个集合中存放一个对象或者基本数据类型的时候,取出来的时候,如果数据类型     * 不匹配,那么就不可避免的需要做一些强制类型转换的工作,那么这就不可避免的出现类型转换异常。     */    public static void main(String[] args) {        List list = new ArrayList();        list.add("aaaa");        //这里我们在取出list集合中的值的时候,如果我们使用了Integer去转换        //这个程序在编码的时候不会报错,但是在运行的时候,就会发生异常,ClassCastException.        //这对于一个大型的程序是有风险的,为了解决这个问题,我们尽量让其在编码期间,程序员就能够去发现这个为问题。        //所以jdk1.5之后出现了泛型 ,详见demo2        Integer integer = (Integer) list.get(0);    }}</span></span>

三:Demo2

<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;import java.util.ArrayList;import java.util.List;/** * 使用泛型的方式,去解决demo1中可能出现的问题 * User: OF895 * Date: 14-7-21 * Time: 下午9:59 */public class Demo2 {    public static void main(String[] args) {        //这里我们限制了使用泛型,所以这个list中只能够存放String类型的参数        //否则在编码期间就会报错        List<String> list = new ArrayList<String>();        list.add("hello world");    }}</span></span>


四:Demo3

<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;import java.util.Set;/** * map集合中使用泛型 * User: OF895 * Date: 14-7-21 * Time: 下午10:07 */public class Demo3 {    public static void main(String[] args) {        Map<Integer, String> map = new HashMap<Integer, String>();        map.put(1, "aa");        map.put(2, "bb");        map.put(3, "cc");        map.put(4, "dd");        map.put(5, "ee");        /**         *         * 取出map集合中存放的值         */        Set<Map.Entry<Integer, String>> set = map.entrySet();        Iterator<Entry<Integer, String>> iterator = set.iterator();        while (iterator.hasNext()) {            Entry<Integer, String> entry = iterator.next();            //通过迭代得出的key            Integer key = entry.getKey();            //通过迭代得出的value            String value = entry.getValue();            System.out.println(key + " : " + value);        }    }}</span></span>

五:Demo4

<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;import java.util.ArrayList;import java.util.Collection;import java.util.LinkedList;/** * User: OF895 * Date: 14-7-23 * Time: 下午11:11 */public class Demo4 {    public static void main(String[] args) {        save(new ArrayList<String>());        save(new LinkedList<Integer>());    }    //当我们在设计一个方法的时候,如果不确定方法的调用者,会传入声明类型的参数    //那么我们可以使用“?”这个通配符    //注意点:使用通配符的时候的注意点:    //不能再使用“参数”与“具体类型”相关的方法。    public static void save(Collection<?> c){         //切记:这里就不能这么写了,为什么?         //其实很好理解,因为我们不确定这个方法的调用者,会传入什么类型的参数         //如果这里直接添加的是String类型的,而上面调用save()方法时候,传入的是Integer类型,怎么办?       // c.add("hello world");    }}</span></span>

六:泛型声明在“类”上面。(需要先声明,再使用)

<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;/** * 自定义泛型类 * <p/> * <p/> * 为什么要将泛型声明在类上面? * 1 当我们一个类中,多个方法使用到同一种类型的泛型的时候(比如T泛型),避免在每一个方法上面都声明T,那么我们 * 可以考虑将泛型声明放在类上面,这样在方法上就可以不声明了(注意必须是这个类中的方法) * <p/> * 2当泛型被声明在类上面的时候,它的作用域仅限于这个类里面。 * <p/> * 3声明在类上面的方法,只可以在类中的“非静态”方法上面使用,静态方法如果需要使用泛型,那么仍然需要自己去声明,然后再使用。 * <p/> * User: OF895 * Date: 14-7-22 * Time: 上午12:10 */public class GenericClass<T> {    public void methodA(T t) {        System.out.println("这里的泛型T,因为定义在了类上面,所以这个类里面的方法都是可以使用这个泛型的");    }    public <E> void methodB(E e) {        System.out.println("这里的泛型E,因为没有在类上面声明,所以在使用之前需要在方法上先声明");    }    public static <T> T methodC(T t) {        System.out.println("在泛型类上面声明的泛型变量,是不可以直接使用在“静态方法”上面的,需要我们自己在方法上面去声明");        return null;    }}</span></span>

七:泛型声明在“方法”上

<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;/** * 泛型方法的使用(自定义泛型方法) * User: OF895 * Date: 14-7-21 * Time: 下午11:26 */public class GenericMethod {    /**     * 自定义泛型方法     * <T> 这个代表声明T,在java中,任何变量使用之前,都需要先声明,然后才可以使用,这是一种语法。     * <p/>     * T:代表的是方法methodA的返回值类型是T,也就意味着我们在参数T中传入声明类型,那么返回值就是什么类型。     */    public <T> T methodA(T t) {        T obj;        obj = t;        return obj;    }    public <T, E, F> void methodB(T t, E e, F f) {        System.out.println("在自定义泛型方法中,我们是可以定义多个泛型参数的,只需要在使用之前都声明一下就可以了");    }}</span></span>

八:自定义“泛型”方法的一个例子(自定义一个reverse的方法,接受任意类型的数组,数组的元素顺序颠倒)

<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;import java.util.Arrays;import java.util.Collections;import java.util.List;/** * 自定义一个reverse的方法,接受任意类型的数组,数组的元素顺序颠倒。 * User: OF895 * Date: 14-7-21 * Time: 下午11:56 */class ReverseArray {    public <T> void reverse(T[] t){        List list = Arrays.asList(t);        Collections.reverse(list);        for(Object i: list){            System.out.println(i);        }    }}</span></span>

测试上面的代码:

<span style="font-size:18px;"><span style="font-size:18px;">public class Test {    public static void main(String[] args) {        String[] arr = new String[]{"a","b","c","d","e"};        Integer[] arrInt = new Integer[]{1,2,3,4,5,6};        ReverseArray reverseArray = new ReverseArray();        reverseArray.reverse(arrInt);    }}</span></span>

九:“增删改查”的一个“基类”(使用泛型的好例子)

JavaBean:

<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.advance;/** * User: OF895 * Date: 14-7-23 * Time: 下午10:01 *///“种类”的javabeanpublic class Category {}</span></span>

BaseDao:

<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.advance;import org.hibernate.Session;/** * * 一个增删改查的基类 * User: OF895 * Date: 14-7-23 * Time: 下午9:24 */public class BaseDao<T> {   private Session session;   private Class clazz;    //如果某个子类实现了BaseDao,那么就调用一下这个构造方法    //掺入具体子类的class对象    public BaseDao(Class clazz) {        this.clazz = clazz;    }    //增加    public void add(T t){        session.save(t);    }    //查找    public T find(String id){        //1在hibernate中,要查找某个对象,是根据传入的id,来作为条件的        //但是这里有个问题,在查找获得这个对象的时候,我们需要传入“被查找”对象的class,但这个是无法通过泛型T,t.getClass或者t.class获得,泛型不能这么使用        //那么如何解决这个问题?        //方法:使用构造方法,由使用这个类的子类去传入。        return (T) session.get(clazz,id);    }    //更新    public void update(T t){        session.update(t);    }    //删除    public void delete(String id){        //在hibernate框架中,要实现删除,需要两步走        //1首先根据传入的id,“查找”出这个对象        //2然后再“删除”这个对象。        T t = (T) session.get(clazz,id);        session.delete(t);    }}</span></span>

CategoryDao:(这里在)

<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.advance;/** * User: OF895 * Date: 14-7-23 * Time: 下午10:01 *//** * * 这里这个CategoryDao只要继承了BaseDao,那么它就拥有了“增删改查”的方法,这也是面向对象的设计方法 */public class CategoryDao extends BaseDao<Category> {    //因为我们在使用BaseDao中的“增删改查”的方法的时候,需要传入具体子类的class类型,指明是那个具体的子类。    //所以这里我们通过子类调用父类的构造函数的方式去传入。    //思考:这样虽然可以实现不同对象都调用BaseDao的方法,但是代码不够优雅。需要显示调用父类的构造方法,传入字节码文件,有更优雅的实现方式。    public CategoryDao() {        super(CategoryDao.class);    }}</span></span>

十:

javabean:

<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.newAdvance;/** * User: OF895 * Date: 14-7-23 * Time: 下午10:33 */public class Book {}</span></span>

NewBaseDao:(这里优化了例子9中的代码,提高了代码的优雅性,我们不需要在子类中的构造方法中传入类型了)

<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.newAdvance;import org.hibernate.Session;import java.lang.reflect.ParameterizedType;/** * 一个基类:实现增删改查 * User: OF895 * Date: 14-7-23 * Time: 下午10:20 */public class NewBaseDao<T> {    //org.hibernate.Session,通过这个类可以使用“增删改查”    private Session session;    private Class clazz;    public NewBaseDao() {        Class cla = this.getClass();//这里首先要明白一点:当子类实现这个父类的时候,在子类new对象的时候,会隐式的调用父类的“无参”构造方法,那么这里this指的就是子类,所以这里的class也是“子类”的class.        ParameterizedType parameterizedType = (ParameterizedType) cla.getGenericSuperclass();//这个方法获得的就是父类的“泛型类型”,类似于BaseDao<Category>,或者BaseDao<Book>,这个后面的参数就是由具体的子类去决定的。        /**         *  不能写成: clazz = parameterizedType.getActualTypeArguments()[0].getClass;         *         *  这里为什么要写成parameterizedType.getActualTypeArguments()[0]?         *         * 因为 NewBaseDao<Category,Book>,这个后面的实际的泛型对象,可能有多个,我们需要显示指明具体的哪一个泛型。         */        //通过这样的构造方法,那么我们子类在实现这个NewBaseDao的时候,就不需要通过构造函数参入class了,因为父类,会自己通过反射去查找,这样代码更加优雅。        clazz = (Class) parameterizedType.getActualTypeArguments()[0];        System.out.println(clazz);    }    //增加    public void add(T t) {        session.save(t);    }    //查找    public T find(String id) {        //1在hibernate中,要查找某个对象,是根据传入的id,来作为条件的        //但是这里有个问题,在查找获得这个对象的时候,我们需要传入“被查找”对象的class,但这个是无法通过泛型T,t.getClass或者t.class获得,泛型不能这么使用        //那么如何解决这个问题?        //方法1:使用构造方法,由使用这个类的子类去传入。        //方法2:当某个“子类”去实现这个“基类”的时候,由基类自己去获得子类的class类型        return (T) session.get(clazz, id);    }    //更新    public void update(T t) {        session.update(t);    }    //删除    public void delete(String id) {        //在hibernate框架中,要实现删除,需要两步走        //1首先根据传入的id,“查找”出这个对象        //2然后再“删除”这个对象。        T t = (T) session.get(clazz, id);        session.delete(t);    }}</span></span>

BookDao:

<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.newAdvance;/** * User: OF895 * Date: 14-7-23 * Time: 下午10:34 */public class BookDao extends NewBaseDao<Book> {    public static void main(String[] args) {        //在BaseDao中,做了操作后,这里在new子类对象的时候,就会默认调用“父类”构造方法了        BookDao bookDao = new BookDao();    }}</span></span>





0 0
原创粉丝点击