Java 泛型在实际开发中的应用

  java泛型是对Java语言的类型系统的一种扩展,泛型的本质就是将所操做的数据类型参数化。下面我会由浅入深地介绍Java的泛型。java

一:泛型出现的背景

在java代码里,你会常常发现相似下边的代码:linux

public class Test {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("hah");
        //list.add(new Test());
       // list.add(1);
        for (Object object : list) {
            String s1 = (String)object;
            //.....若是是你你该如何拿出list的值,若是list中放着上边的不一样类型的东西。无解
        }
    }
}

  编码的时候,不加泛型是能够的,可是 你从容器中拿出来的时候必须强制类型转换,第一是多敲不少代码,第二极容易发生类型转换错误,这个运行时异常 好比你把上边程序员

注释的代码放开,程序在获取容器的地方就会报运行时异常 ClassCasrException编程

Java语言的设计者引入了泛型,暂时先不追究它内在是怎么实现的。只须要知道,若是咱们像下边这么写,咱们就不须要强制类型转换。咱们也不须要担忧运行是异常了。数组

List<String> newList = new ArrayList<String>();
newList.add("hhe");
newList.add("123");
String s1 = newList.get(0);//不须要强制类型转换,由于我加了泛型,我就认为它里边必定都是String

二: 泛型的语法使用

1:使用具体的泛型类型: 尖括号内带有具体的类型。能够限定这个Map的key和value只能是字符串

Map<String, String> map = new HashMap<String, String>();
map.put("key","value");
String value = map.get("key")

从面向对象的角度看,使用对象的时候,泛型内传入的具体的类型。声明的时候采用尖括号内加占位符的形式,好比这是HashMap的源码安全

public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable{
    ...
   public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                      DEFAULT_INITIAL_CAPACITY),     DEFAULT_LOAD_FACTOR);
        putAllForCreate(m);
    }  
    ...
      public V remove(Object key) {
        Entry<K,V> e = removeEntryForKey(key);
        return (e == null ? null : e.value);
    }   
}

 2:方法声明的时候 : public  <T> T getValue(){...}

  在上边的代码中,咱们能够看到在类上如何定义泛型,也看到了类上定义的占位符在类的普通方法上能够直接使用。可是若是想在静态方法上定义泛型,这须要单独的处理  。下面咱们单独对方法上如何定义ide

和使用泛型进行介绍(注意:方法上是否认义泛型和类上是否认义没有必然的联系)this

好比Web项目中,泛型是修饰类型的,在方法上,通常就是返回值和参数列表编码

  •   返回值类型:能够定义为List<String>等形式,可是实际开发中,通常都是不知道具体类型,定义形式以下  <T> List<T> test(T t){...} ,前边的<T>能够理解为泛型的声明,你只有声明了T,你才能够在              方法中用到T,这一具体的类型, List<T>是具体的返回值类型。
  •   方法传参: 能够用占位符限定的容器 好比 List<T>,或者直接是占位符 T  
public class BaseServiceImpl implements BaseService {
    protected <T> List<T> calcPage(String hql, PageContext pageContext,
            Object... params) {
        int total = getDataTotalNum(hql, params);
        pageContext.setTotal(total);
        List<T> list = (List<T>) getPageDataByHQL(hql, pageContext.getRows(),
                pageContext.getPage(), pageContext.getTotal(), params);
        return list;
    }
    @Override
    @Sync
    public void deleteBatchVO(final List<?> dataList) throws ServiceException {
        baseDAO.deleteBatchVO(dataList);
    }
    @Override
    public List<?> getPageDataByHQL(final String hql,
            final Map<String, Object> filter) throws ServiceException {
        return baseDAO.getPageDataByHQL(hql, filter);
    }
}

简单的例子:spa

public <T> T TestG(T t){
        return t;
    }

 方法定义的时候,泛型是这样设计,在使用的时候,代码以下:

List<TaclUserinfo> list = calcPage(hqluser1.toString(), pageContext,
                taclRole.getRoleid(), taclRole.getTenantId());
//返回值类型 是<T>List<T>的,j接收的时候,我直接用List<具体类>

3 :类或者接口使用泛型  interface Collection<V> {..}

  上边的HashMap代码中,也看到了在类上使用泛型的具体例子。在真正的项目上,一些基础的公共类常常定义泛型,以下:

 1 public interface GenericDao<T, ID extends Serializable> {
 2 
 3     public abstract void saveOrUpdate(T t) throws DataAccessException;
 4 
 5     public abstract T get(ID id) throws DataAccessException;
 6 
 7     public abstract List<T> query(String queryString) throws DataAccessException;
 8 
 9     public abstract Serializable save(T t) throws DataAccessException;
10 
11     public abstract void saveOrUpdateAll(Collection<T> entities) throws DataAccessException;
12 
13     public abstract List<T> loadAll() throws DataAccessException;
14 
15     public abstract void merge(T t) throws DataAccessException;
16 
17 }    

接口的实现类: 传入参数为T,实现类中也能够继续用T,返回为T也能够用T;实现的时候 能够用 extends限定泛型的边界。

public abstract class GenericDaoImpl<T extends BaseEntity, ID extends Serializable> extends
        HibernateDaoSupport implements GenericDao<T, ID> {
    
    public void merge(T t) throws DataAccessException {
        TenantInterceptor.setTenantInfoToEntity(t);
        getHibernateTemplate().merge(t);
    }
    
         public T get(ID id) throws DataAccessException {
        // getHibernateTemplate().setCacheQueries(true);
        T load = (T) getHibernateTemplate().get(getEntityClass(), id);
        return load;
    }    
}

具体使用的时候:

public class UserDao extends GenericDaoImpl<User, Serializable> {
    ...//好比get() merge()这些方法不须要在单独编写,直接调用
}    

4:  声明带边界的泛型   class userDao<T extends BaseEntity>

  Java中泛型在运行期是不可见的,会被擦除为它的上级类型。若是你是无界的泛型参数类型,就会被替换为Object. 

public class RedColored<T extends Color> {
    public T t;
    public void color(){
        t.getColor();//T的边界是Color,因此能够调用getColor(),不然会编译报错
    }
}

abstract class Color{
    abstract void getColor();
}

   相似这样的定义形式:GenericDaoImpl<T extends BaseEntity, ID extends Serializable> ,java重载了extends,标注T的边界就是BaseEntity。若是需求是继承基类,那么边界定义在子类上

相似 

class Colored2<T extends Color> extends RedColor<T>

5:用于通配符  <?>

   参考于( Java 通配符解惑  )泛型类型的子类型的不相关性。好比 如今List<Cat>并非List<Anilmal>是两种不一样的类型;且无继承关系 。那么,咱们像想要传入的参数既多是List<Cat>

也有多是List<Annimal>

public class AnimalTrainer {
    public void act(List<? extends Animal> list) {
//备注:若是用 List<Animal> 做为形参列表,是没法传入List<Cat>for (Animal animal : list) {
            animal.eat();
        }
    }
}

  act(List<? extends Animal> list),当中“?”就是通配符,而“? extends Animal”则表示通配符“?”的上界为Animal,换句话说就是,“? extends Animal”能够表明Animal或其子类,可表明不了Animal的父类(如Object),由于通配符的上界是Animal。

因此,泛型内是不存在父子关系,可是利用通配符能够产生相似的效果:

假设给定的泛型类型为G,(如List<E>中的List),两个具体的泛型参数X、Y,当中Y是X的子类(如上的Animal和Cat))

  • G<? extends Y> 是 G<? extends X>的子类型(如List<? extends Cat> 是 List<? extends Animal>的子类型)。
  • G<X> 是 G<? extends X>的子类型(如List<Animal> 是 List<? extends Animal>的子类型)
  • G<?> 与 G<? extends Object>等同,如List<?> 与List<? extends Objext>等同 

三: 泛型能够用到那些地方

    泛型能够用到容器,方法,接口,内部类,抽象类

四: Java中泛型独特之处

    泛型是Java1.5以后才引入的,为了兼容。Java采用了C++彻底不一样的实现思想。Java中的泛型更多的看起来像是编译期用的,好比我定义一个使用泛型的demo

我在查看它的class文件时,发现class文件并无任何泛型信息。

Java会在编辑期把泛型擦除掉

  在JAVA的虚拟机中并不存在泛型,泛型只是为了完善java体系,增长程序员编程的便捷性以及安全性而建立的一种机制,在JAVA虚拟机中对应泛型的都是肯定的类型,在编写泛型代码后,java虚拟中会把这些泛型参数类型都擦除,用相应的肯定类型来代替,代替的这一动做叫作类型擦除,而用于替代的类型称为原始类型,在类型擦除过程当中,通常使用第一个限定的类型来替换,若无限定,则使用Object.

擦除的原理以及边界

  关键在于从泛型类型中清除类型参数的相关信息,而且再必要的时候添加类型检查类型转换的方法。

  能够参考Java泛型-类型擦除。 运行期编译期会去掉泛型信息,转换为左边界,在调用的地方添加类型转换。

泛型擦除肯可能致使的问题

用泛型不能够区分方法签名

public void test(List<String> ls){
                System.out.println("Sting");
            }
            public void test(List<Integer> li){
                System.out.println("Integer");
            }
//这回报错,编译期没法区分这两个方法

泛型类的静态变量是共享

public class StaticTest{
    public static void main(String[] args){
        GT<Integer> gti = new GT<Integer>();
        gti.var=1;
        GT<String> gts = new GT<String>();
        gts.var=2;
        System.out.println(gti.var);
    }
}
class GT<T>{
    public static int var=0;
    public void nothing(T x){}
}

五: 泛型中特殊使用

   java中的泛型不仅是上述说的内容,还有一些特殊的地方,若是这些地方也用泛型该怎么设计。好比说“动态类型”,“潜在类型”,“异常”

程序若是运行时须要类型信息

  就在调用的地方传入类型信息

异常中使用泛型

  不能抛出也不能捕获泛型类的对象。事实上,泛型类扩展Throwable都不合法,由于泛型信息会被擦除,至关于catch两个相同的异常,是不能够的

数组与泛型

  不能声明参数化类型的数组, 数组能够记住本身的元素类型,不能创建一个泛型数组。(固然 你若是用反射仍是能够建立的,用Array.newInstance。这里说不能建是不能用普通方法)

泛型的一些其余细节:  

  1.基本类型没法做为类型参数即ArrayList<int>这样的代码是不容许的,若是为咱们想要使用必须使用基本类型对应的包装器类型ArrayList<Integer>

  2.在泛型代码内部,没法得到任何有关泛型参数类型的信息换句话说,若是传入的类型参数为T,即你在泛型代码内部你不知道T有什么方法,属性,关于T的一切信息都丢失了(类型信息,博文后续)。

  3.注,在可以使用泛型方法的时候,尽可能避免使整个类泛化。

六:简单归纳

  虚拟机中没有泛型,只有普通类和普通方法

  全部泛型类的类型参数在编译时都会被擦除

  建立泛型对象时请指明类型,让编译器尽早的作参数检查

  要忽略编译器的警告信息,那意味着潜在的ClassCastException等着你。

相关文章
相关标签/搜索