初探Java中的泛型

  泛型是一个很有意思也很重要的概念,本篇将简单介绍Java中的泛型特性,主要从以下角度讲解:

  1.什么是泛型。

  2.如何使用泛型。

  3.泛型的好处。

1.什么是泛型?

泛型,字面意思便是参数化类型,平时所面对的类型一般都是具体的类型,如果String,Integer,Double,而泛型则是把所操作的数据类型当作一个参数。如,ArrayList<String>(),通过传入不同的类型来指定容器中存储的类型,而不用为不同的类型创建不同的类,这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

2.如何使用泛型?

我们先来看看泛型是什么样子的:

public interface List<E> {
   void add(E);
   Iterator<E> iterator();
  }

  这是List接口,这里用E来代替具体类型,这样就可以往里面传入任意类型,也许你要问了,直接使用Object不好吗?我们来用一个栗子比较一下:

  先用非泛型方式来实现一下:

public class ObjHolder {
  private Object a;

  public ObjHolder(Object a) {
    this.a = a;
  }

  public void set(Object a) {
    this.a = a;
  }

  public Object get(){
    return a;
  }

  public static void main(String[] args) {
    ObjHolder holderA = new ObjHolder("Frank");
    System.out.println((String) holderA.get());
    holderA.set(233);
    System.out.println((Integer) holderA.get());
  }
}

  这样就实现了一个包装类,可以用来存取一个任意类型的对象。但是每次取出来都需要进行类型转化,如果方法的参数类型是ObjHolder的话,无法知道它里面存放的对象的确切类型,这样就反而带来很多不必要的麻烦。

  现在来看一下用泛型实现是怎样的:

public class GenericHolder<T> {
  private T obj;

  public GenericHolder(T obj){
    this.obj = obj;
  }

  public T getObj() {
    return obj;
  }

  public void setObj(T obj) {
    this.obj = obj;
  }

  public static void main(String[] args) {
    GenericHolder<String> holderA = new GenericHolder<String>("Frank");
    System.out.println(holderA.getObj());

    //holderA.set(233);无法编译通过,因为只能往holderA中存入String类型
    GenericHolder<Integer> holderB = new GenericHolder<Integer>(233);
    System.out.println(holderB.getObj());
  }
}

  这样通过传入类型信息如String和Integer,来代替其中的泛型参数T,这里的T可以理解为一个占位符,用其他字母也是可以的,一旦传入具体类型,如String,则所有使用T的地方都会用String类型替换。

  对比一下上面两种方式,区别在哪呢?打个比方,不用泛型的实现方式,相当于一个袋子,里面可以装任意类型的黑盒子,你什么都可以往里放,但是你可能不知道你下一个取出来的是什么东西,而泛型的实现方式,相当于一个贴了标签的黑盒子,标签上可以写任何信息,如写上水果,那么这个盒子就只能装水果,你也会知道每次取出来的肯定是水果而不是其它东西,同理类似如写上杂粮,那么这个袋子就只能用来装杂粮,但其实上都是同一种袋子,并不是为每一种类型的东西准备一种袋子。(因为Java的泛型使用了类型擦除机制,至于类型擦除是什么,暂时不做过多介绍,以后会有文章做更详细的说明)。

  泛型被广泛应用在容器类中,如ArrayList<T>() 表示用于存储特定类型的数组,除此之外,还有很多泛型接口,如Comparable<T>。使用泛型能带来极大的便利性。

  在泛型中可以对类型进行限制,如:<T extends Comparable<T>>表示只能传递已经实现了Comparable接口的类型对象,这里是使用extends而不是implement,而且对于接口也只能写一个。<T extends Number>表示只能接收Number类或者其子类的对象。与之相反的边界通配符是super,如:<T extends Phone>表示只能接收类型为Phone或其父类的对象。

  在使用extends和super的时候需要特别注意,因为使用它们是有副作用的,比如:

  List<T extends Number> list = new ArrayList<Number>();
  list.add(4.0);//编译错误
  list.add(3);//编译错误

  因为泛型是为了类型安全设计的,如果往List<? extends Number> list 塞值的话,在取的时候就无法确认它到底是什么类型了,编译器只知道它是Number类型或者它的派生类型,但无法确定是哪个具体类型。通配符T表示其中存的都是同一种类型,因此使用extend下边界的话是无法进行存操作的。同理super下边界是不能取值的。

  那什么时候该用extends,什么时候该用super呢?先说结论:

   PECS原则:

  1. 频繁往外读取内容的,适合用上界Extends。
  2. 经常往里插入的,适合用下界Super。

3.泛型的好处?

  泛型看起来很炫酷,但初看起来,好像没什么卵用?客官且慢,进屋里坐(滑稽)。

  使用泛型的好处我们来一项一项列出来:

       1,类型安全。

  这是最显而易见的,泛型的主要目标是提高 Java 程序的类型安全。通过使用泛型定义的变量的类型限制,可以很容易实现编译期间的类型检测,避免了大量因为使用Object带来的不必要的类型错误。

  没有泛型,这些对Object变量的类型假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中),而且每次使用前还需要进行不安全的强制类型转换。

  2,代码复用。

  泛型的一个很大好处就是增加了代码的复用性,比如上面的 GenericHolder 类,就能存取任意类型的对象,而不用为每种类型写一个包装类。

  3,潜在的性能收益。

  泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改。所有工作都在编译器中完成,编译器生成类似于没有泛型(和强制类型转换)时所写的代码,只是更能确保类型安全而已。Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

  至此,本篇讲解完毕,如果想要更好的理解,还需要多写代码,在实践中去应用。

  欢迎大家继续关注!

以上就是初探Java中的泛型的详细内容,更多关于Java 泛型的资料请关注我们其它相关文章!

(0)

相关推荐

  • JAVA泛型的继承和实现、擦除原理解析

    这篇文章主要介绍了JAVA泛型的继承和实现.擦除原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 很多的基础类设计会采用泛型模式,有些应用在使用的时候处于隔离考虑,会进行继承,此时子类如何继承泛型类就很讲究了,有些情况下需要类型擦除,有些情况下不需要类型擦除,但是大多数情况下,我们需要的是保留父类的泛型特性.因为类型被擦除后,得到的对象会是Object,此时会导致编码或反序列化失败. 如下所示: 如果子类也继承父类的泛型化,则类型不会被擦

  • Java泛型<T> T与T的使用方法详解

    泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样. 在集合框架(Collection framework)中泛型的身影随处可见.例如,Map 类允许向一个 Map 类型的实例添加任意类的对象,即使最常见的情况在给定映射(map)中保存一个string键值对. 命名类型参数 对于常见的泛型模式,推荐的泛型类型

  • Java泛型机制必要性及原理解析

    泛型程序设计(Generic Programming) "使用泛型机制编写的程序代码要比那些杂乱地使用Object变量,然后再进行强制类型转换的代码具有更好的安全性和可读性.泛型对于集合类尤其有用." 1.意义.必要性.重要性 泛型程序设计 意味着编写的代码可以被很多不同类型的对象所重用.例如,我们并不希望为聚集String和File对象分别设计不同的类.实际上,也不需要这样做,因为一个ArrayList类就可以聚集任何类型的对象.这是一个泛型程序设计的实例. 事实上,在Java增加泛

  • Java让泛型实例化的方法

    泛型对象可以实例化吗? 不可以,T t=new T()是不可以的,编译器会报错.由于泛型擦除,编译器在编译时无法确定泛型所对应的真实类型 解决方法 使用反射新建实例 Type superclass = getClass().getGenericSuperclass(); ParameterizedType parameterizedType = null; if (superclass instanceof ParameterizedType) { parameterizedType = (Pa

  • Java 泛型全解析

    泛型简介 什么是泛型? 参化类型,数是JDK1.5的新特性.(定义泛型时使用参数可以简单理解为形参),例如List<E>,Map<K,V> 编译时的一种类型,此类型仅仅在编译阶段有效,运行时无效.例如List<String>在运行时String会被擦除,最终系统会认为都是Object. 为什么要使用泛型? 泛型是进行类型设计或方法定义时的一种约束规范,基于此规范可以: 提高编程时灵活性(有点抽象,后续结合实例理解). 提高程序运行时的性能.(在编译阶段解决一些运行时需要

  • 浅谈三分钟学习Java泛型中T、E、K、V、?的含义

    泛型是Java中一个非常重要的内容,对于Java进阶学习是必须要掌握的知识点之所以说这个知识点重要,如果你有过阅读过一些开源框架的代码,那你一定会看到源码中有很多地方使用到了泛型. 随便举两个例子,一个List,一个Map. 看了上面的源码,简单聊一下泛型,也就是回顾一下泛型的相关知识,来源百度百科. [泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法.Java语

  • 详细分析Java 泛型的使用

    一.泛型的简介 1.为什么要使用泛型? 一般使用在集合上,比如现在把一个字符串类型的值放入到集合里面,这个时候,这个值放到集合之后,失去本身的类型,只能是object类型.这时,如果想要对这个值进行类型转换,很容易出现类型转换错误,怎么解决这个问题,可以使用泛型来解决. 2.在泛型里面写是一个对象,String 不能写基本的数据类型 比如int,要写基本的数据类型对应的包装类 基本数据类型 对应包装类 基本数据类型 对应包装类 byte Byte short Short int Integer

  • Java泛型extends及super区别实例解析

    <? extends T>和<? super T>是Java泛型中的"通配符(Wildcards)"和"边界(Bounds)"的概念. <? extends T>:是指"上界通配符(Upper Bounds Wildcards)" <? super T>:是指"下界通配符(Lower Bounds Wildcards)" 为什么要用通配符和边界? 使用泛型的过程中,经常出现一种很

  • Java不可不知的泛型使用示例代码

    本文介绍了Java的泛型的基本使用. 1. 为什么使用泛型 看下面一个例子: 为了说明问题,本类写的尽量简陋,请把目光主要放在类型上. /** * @author Xing Xiaoguan (xingrenguanxue) */ public class MyArrayList { private int[] elementData; private int size = 0; public MyArrayList(int capacity) { elementData = new int[c

  • 深入分析JAVA 反射和泛型

    从 JDK5 以后,Java 的 Class 类增加了泛型功能,从而允许使用泛型来限制 Class 类,例如,String.class 的类型实际上是 Class<String>.如果 Class 对应的类暂时未知,则使用 Class<?>.通过在反射中使用泛型,可以避兔使用反射生成的对象需要强制类型转换. 泛型和 Class 类 使用 Class<T> 泛型可以避免强制类型转换.例如,下面提供一个简单的对象工厂,该对象工厂可以根据指定类来提供该类的实例. public

  • Java泛型变量如何添加约束

    有时,类或方法需要对类型变量加以约束.下面是一个典型的例子,我们要寻找数组中的最小元素: public class ArrayAlg { public static <T extends Comparable> T min(T[] array){ if (array == null || array.length == 0){ return null; } T smallest = array[0]; for (int i=0;i<array.length;i++){ if (small

随机推荐