范型的使用和设计

  java泛型是JDK1.5以后出现的新特性,泛型的简单使用(例如在集合中的使用)本文不做讲解,这里主要讲解一下泛型的设计。

一、泛型类设计

  在定义泛型类时,使用尖括号来指定泛型类型参数,泛型类型参数与方法参数不同,方法类型参数传入的是值,而泛型类型参数传入的是类的class类型。请看以下代码:

// 父类animal  
abstract class Animal{  
    public abstract void eat();  
}  

// 子类cat  
class Cat extends Animal{  
    @Override  
    public void eat() {  
        System.out.println("eat fish");  
    }  
}  

// 子类dog  
class Dog extends Animal{  
    @Override  
    public void eat() {  
        System.out.println("eat bone");  
    }  
}  

// 泛型类Generic  
class Generic<T>{  
    private T t;  

    public Generic(T t){  
        this.t = t;  
    }  

    public void doEat(){  
        if (t instanceof Dog){  
            Dog d = (Dog)t;  
            d.eat();  
        } else if(t instanceof Cat){  
            Cat c = (Cat)t;  
            c.eat();  
        }  
    }  
}     

  此时,如果调用
Generic c = new Generic(new Cat());
c.doEat();
  那么输出结果肯定是 “eat fish”,但是如果调用
Generic c = new Generic(new Dog());
  肯定会编译出错,因为Generic的泛型参数为Cat,因此通过设置泛型参数,可以实现对方法参数类型的限定。那么有人会说,限定方法参数,看不出有什么优势,而且直接在Generic的doEat方法参数中,改成Cat(public void doEat(Cat c))就可以了。这样确实也限制了,但是如果我们此时期望Generic传入Dog类型的对象,或者其他(例如String)类型的对象,那么这种解决方案就无效了,此时只能通过泛型来解决,例如要传入Dog类型的参数,则:
Generic c = new Generic(new Dog());
c.doEat();
  说到这里,问题又来了,如果此时我期望传入的类型只能是Dog和Cat,不能其它类型,该如何解决?那么就说道另一个知识点,泛型类型的限定,代码如下:

class Generic<T extends Animal>{  
    private T t;  

    public Generic(T t){  
        this.t = t;  
    }  

    public void doEat(){  
        if (t instanceof Dog){  
            Dog d = (Dog)t;  
            d.eat();  
        } else if(t instanceof Cat){  
            Cat c = (Cat)t;  
            c.eat();  
        }  
    }  
}  

  通过extends关键字,我们将T的类型限定为Animal的子类,又因为Dog和Cat继承自Animal,所以目前Ceneric的泛型类型只能传入Dog和Cat,如果我们传入其他类型,例如String

    Generic<String> c = new Generic<String>(new String());

  那么这行代码将会编译出错,因为String不是animal的子类。这种限定泛型参数的技巧常用在泛型DAO的设计中,众所周知,j2ee项目中,我们会有很多张表的数据交互,不论有什么样的复杂业务,总少不了单表的增删改查,所以我们会将单表的增、删、该、查的基础方法抽取出来放入一个父类DAO,然后其他业务模块的DAO继承父类DAO,在没有使用泛型之前,他们的类图如下:
这里写图片描述

  通过类图,大致可以了解,为了将共有的增删改查方法抽取出来,必须将save方法的参数和load方法的返回值设置成Object,一旦设置成Object,那么代码中就会充斥着大量的类型判断和类型强转的内容,因此才使用泛型解决这一问题(泛型DAO设计常用有两种方式,本文先介绍其中一种),泛型DAO代码如下:

public interface BaesDAO<T extends BaseEntity>{  
    /* 
     *  通过ID查询对象 
     */  
    public T Load(Integer id);  
    /* 
     *  新增 
     */  
    public void save(T entity);  
    /* 
     *  修改 
     */  
    public void udate(T entity);  
    /* 
     *  删除 
     */  
    public void delete(Integer id);  
}  
/*  
 * 这里类似伪代码,大家能明白意思就好 
 */  
public class BaseDAOImpl<T extends BaseEntity> implements IBaseDAO<T extends BaseEnt>{  
public T load(Integer id){  
    }  
    public void save(T entity){  
    }  
    public void update(T entity){  
    }  
    public void delete(integer id){  
    }  
}  
重点看具体业务模块的DAO
public interface IUserDAO extends IBaseDAO<UserEntity>{  
    public List<UserEntity> findUserByCompany(String companyId);  
}  


public class UserDAOImpl extends BaseDAOImpl<UserEntity> implements IUserDAO{  
    public List<UserEntity> findUserByCompany(String companyId){  

    }      
}  

  在UserDAOImpl中,当继承BaseDAOImpl时传入的泛型为UserEntity,由于UserEntity继承BaseEntity,所以可以传入,又由于传入的是UserEntity,所以在BaseDAOImpl的save方法中,存入的就是UserEntity对象,同样load返回的对象就是UserEntity,通过泛型,减少了对象类型的判断以及类型强制转换,简化了代码,增强了可读性。

二、泛型方法设计

  泛型方法设计,主要是针对方法参数和返回值进行泛型的设计,请看以下代码:

public List<Animal> test(List<Animal> list){  
     return null;  
} 

  先看该方法的参数,要求传入一个List《Animal》 list,但如果此时我期望传入一个List《Cat》 list该怎么办?也许你会有一些误解,认为Cat是Animal的子类,所以我们可以传入一个List Cat》 list(即new ArrayList《Cat》() 传进去),这是完全错误的,请看在eclipse中的代码:
这里写图片描述

  为了解决以上问题,又涉及到了新的知识点,通配符。通配符有两个关键字extends和super,先看extends,extends表示类型向下限制,请看代码:
这里写图片描述

  通过代码我们可以理解,之所以extends叫向下限制,是因为?表示要初始化的泛型类型必须是extends后面类型的子类型,所以我们可以在GenericMethodTest的方法里面参数改为:List《? extends Animal》,这样我们就可以传入List《Cat》或者List《Dog》了。请看代码:
这里写图片描述

  此时,不会在编译报错了。同理,对于方法的返回值,如果我们也期望返回的List泛型为Animal的子类,那么我们只需要把返回类型声明为List《? extends Animal》,请看代码:
这里写图片描述

  最后,我们再说一下super,super和extends相反,表示对泛型类型的向上类型限制,例如:
List《? super Animal》 list = new ArrayList《Object》();
List《? super Animal》 list2 = new ArrayList《Animal》();
List《? super Animal》 list3 = new ArrayList《Cat》();// 此处会编译出错
  如果我们在声明时使用? super Animal,那么?就表示必须是super关键字后面类类型的父类型或本身自己的类型。那super的主要使用场景在哪里?例如我有一个list,即想存入Cat,也想存入Dog,那么我的List的泛型类型必须是Cat和Dog的父类,例如Animal或者Object,请看代码:
这里写图片描述

  通过上面代码,我们得知,如果想一个list即存入dog,也存入cat,那么该list的泛型至少是一个animal,那此时,就可以通过List《? super Animal》 来实现。那么在方法设计中,有时候我们的业务逻辑需要返回的结果集中即包含Dog,也包含Cat,那么我们就可以把方法的返回值设置为List《? super Animal》,请看代码:
这里写图片描述

  通过对泛型方法设计的讲解,我们再对泛型通配符super和extends进行总结。在上面提到的三个类,Animal,Dog和Cat,他们的类关系如下,
这里写图片描述

  虽然这三个类有如上继承关系,但是不代表他们的泛型有继承关系,请看下图:
这里写图片描述

  但是一旦使用了通配符extends,那么泛型之间可以看似有继承关系(记住,只是看似有),请看下图:
这里写图片描述

  如果使用了super,那么表示当前泛型对象是super后面类型的父类型,也可以看似有继承关系,请看下图:
这里写图片描述

  泛型的类设计和方法设计大致介绍到这里,还有一些泛型的高级应用后续在补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值