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

一:泛型出现的背景

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

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(){...}

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

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

比如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);
}
}

简单的例子:

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代码中,也看到了在类上使用泛型的具体例子。在真正的项目上,一些基础的公共类经常定义泛型,如下:

 public interface GenericDao<T, ID extends Serializable> {

     public abstract void saveOrUpdate(T t) throws DataAccessException;

     public abstract T get(ID id) throws DataAccessException;

     public abstract List<T> query(String queryString) throws DataAccessException;

     public abstract Serializable save(T t) throws DataAccessException;

     public abstract void saveOrUpdateAll(Collection<T> entities) throws DataAccessException;

     public abstract List<T> loadAll() throws DataAccessException;

     public abstract void merge(T t) throws DataAccessException;

 }    

接口的实现类: 传入参数为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等着你。

Java 泛型在实际开发中的应用的更多相关文章

  1. Java 反射在实际开发中的应用

    运行时类型识别(RTTI, Run-Time Type Information)是Java中非常有用的机制,在java中,有两种RTTI的方式,一种是传统的,即假设在编译时已经知道了所有的类型:还有一 ...

  2. [转]Java 反射在实际开发中的应用

    一:Java类加载和初始化 1.1 类加载器(类加载的工具) 1.2 Java使用一个类所需的准备工作 二:Java中RTTI 2.1 :为什么要用到运行时类型信息(就是RTTI) 2.2  :RTT ...

  3. Android学习探索之Java 8 在Android 开发中的应用

    前言: Java 8推出已经将近2年多了,引入很多革命性变化,加入了函数式编程的特征,使基于行为的编程成为可能,同时减化了各种设计模式的实现方式,是Java有史以来最重要的更新.但是Android上, ...

  4. Java 数据类型在实际开发中应用

    在前边的博文中,我已经介绍了Java核心的容器IO等,现在我来说一下java中的数据类型.在java中,一切东西皆为对象(这句话意思是java中绝大数情况都用对象),极少数不是对象的,也存在与之对应的 ...

  5. Java IO在实际开发中的应用

    IO是java绕不过去的槛,在开发中io无处不在, 正如同 世界上本没有路,java io写多了,也就知道了大体是什么意思,在读完thinking in java 感觉就更清晰了,结合具体的业务场景, ...

  6. Java和.NET在开发中的不同盘点

    我是用VS2008和VS2010开发.NET程序,通过MyEclipse8.5开发JAVA程序,下面从IDE.语言.插件的不同点来做下简单的说明.但由于经验知识还有限,本篇文章只能从比较表面的以及自己 ...

  7. Java数据类型在实际开发中的应用二枚举类型

    在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的.在JDK1.5之前,人们用接口来描述这一种数据类型. 1. ...

  8. Java数据类型在实际开发中的应用一

    在前边的博文中,我已经介绍了Java核心的容器IO等,现在我来说一下java中的数据类型.在java中,一切东西皆为对象(这句话意思是java中绝大数情况都用对象),极少数不是对象的,也存在与之对应的 ...

  9. Java 数据类型在实际开发中应用二枚举

    在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的.在JDK1.5之前,人们用接口来描述这一种数据类型. 1. ...

随机推荐

  1. 【css3网页布局】flex盒子模型

    1.0 前言 网页布局(layout)是CSS的一个重点应用. 经典布局类型: 布局的传统解决方案,基于盒状模型,依赖 display属性 + position属性 + float属性.它对于那些特殊 ...

  2. 第 15 章 可扩展性设计之 Cache 与 Search 的利用

    前言: 前面章节部分所分析的可扩展架构方案,基本上都是围绕在数据库自身来进行的,这样是否会使我们在寻求扩展性之路的思维受到“禁锢”,无法更为宽广的发散开来.这一章,我们就将跳出完全依靠数据库自身来改善 ...

  3. CentOS7下配置网络yum源(附带下载地址)

    一.查看外网是否通畅 配置网络yum源(需要保证外网开通,我这里是使用网易163提供开源镜像站) 二.下载repo文件 cd /etc/yum.repos.dwget http://mirrors.1 ...

  4. THREE笛卡尔右手坐标系详解

    1,正常的笛卡尔右手坐标系,以屏幕右方为+X轴,屏幕上方为+Y轴,垂直屏幕向外为+Z轴,如下图,xy轴组成的平面为屏幕面 但由于THREE里的相机并不总是从屏幕正前方视角,还可以设置坐标系任意一个轴为 ...

  5. inux中shell变量$#,$@,$0,$1,$2的含义

    转自:http://www.cnblogs.com/fhefh/archive/2011/04/15/2017613.html linux中shell变量$#,$@,$0,$1,$2的含义解释: 变量 ...

  6. Lua 设置table为只读属性

    项目中部分只读表易被人误改写,故决定在非线上环境里对这些表附加只读属性,方便在出现误改写的时候抛出lua错误,最终版代码如下: --[[-------------------------------- ...

  7. R实现地理位置与经纬度相互转换

    本实例要实现目标通过输入城市名或者地名,然后找出其经度纬度值,以及通过可视化展现其线路流向以及周边地图展示 address_list数据: 山西省太原市小店区亲贤北街77号 贵州省贵阳市云岩区书香门第 ...

  8. 前端教你学UI——人物处理(一)

    一.序言 本文作为本系列的第一篇写UI的文章,开头还是有必要申明一些东西的,本系列主要是为了作为博主在前端工作之余学习UI的一个记录,同时为了让更多的同行学习到一些编程之外的其他东西.所以本文会尽可能 ...

  9. 一天搞定CSS: 清除浮动(float)--13

    上一节已经说明了为什么要清除浮动了.这里我们就来解决浮动产生的各种问题. 为什么要清楚浮动? 地址:http://blog.csdn.net/baidu_37107022/article/detail ...

  10. badboy录制兼容性有趣测试

    badboy录制默认是启动IE浏览器,使用badboy录制脚本时,遇到测试系统对IE浏览器不兼容时,就需要考虑换浏览器,修改为其他浏览器(如firefox.chrome)录制,只需要设置该浏览器为默认 ...