泛型是一个很有意思也很重要的概念,本篇将简单介绍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 super 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语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

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

  欢迎大家继续关注!

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan

【Java入门提高篇】Day14 Java中的泛型初探的更多相关文章

  1. 【Java入门提高篇】Java集合类详解(一)

    今天来看看Java里的一个大家伙,那就是集合. 集合嘛,就跟它的名字那样,是一群人多势众的家伙,如果你学过高数,没错,就跟里面说的集合是一个概念,就是一堆对象的集合体.集合就是用来存放和管理其他类对象 ...

  2. 【Java入门提高篇】Day13 Java中的反射机制

    前一段时间一直忙,所以没什么时间写博客,拖了这么久,也该更新更新了.最近看到各种知识付费的推出,感觉是好事,也是坏事,好事是对知识沉淀的认可与推动,坏事是感觉很多人忙于把自己的知识变现,相对的在沉淀上 ...

  3. 【Java入门提高篇】Day1 抽象类

    基础部分内容差不多讲解完了,今天开始进入Java提高篇部分,这部分内容会比之前的内容复杂很多,希望大家做好心理准备,看不懂的部分可以多看两遍,仍不理解的部分那一定是我讲的不够生动,记得留言提醒我. 好 ...

  4. 【Java入门提高篇】Day21 Java容器类详解(四)ArrayList源码分析

    今天要介绍的是List接口中最常用的实现类——ArrayList,本篇的源码分析基于JDK8,如果有不一致的地方,可先切换到JDK8后再进行操作. 本篇的内容主要包括这几块: 1.源码结构介绍 2.源 ...

  5. 【Java入门提高篇】Day16 Java异常处理(下)

    今天继续讲解java中的异常处理机制,主要介绍Exception家族的主要成员,自定义异常,以及异常处理的正确姿势. Exception家族 一图胜千言,先来看一张图. Exception这是一个父类 ...

  6. 【Java入门提高篇】Day31 Java容器类详解(十三)TreeSet详解

    上一篇很水的介绍完了TreeMap,这一篇来看看更水的TreeSet. 本文将从以下几个角度进行展开: 1.TreeSet简介和使用栗子 2.TreeSet源码分析 本篇大约需食用10分钟,各位看官请 ...

  7. 【Java入门提高篇】Day28 Java容器类详解(十)LinkedHashMap详解

    今天来介绍一下容器类中的另一个哈希表———>LinkedHashMap.这是HashMap的关门弟子,直接继承了HashMap的衣钵,所以拥有HashMap的全部特性,并青出于蓝而胜于蓝,有着一 ...

  8. 【Java入门提高篇】Day27 Java容器类详解(九)LinkedList详解

    这次介绍一下List接口的另一个践行者——LinkedList,这是一位集诸多技能于一身的List接口践行者,可谓十八般武艺,样样精通,栈.队列.双端队列.链表.双向链表都可以用它来模拟,话不多说,赶 ...

  9. 【Java入门提高篇】Day26 Java容器类详解(八)HashSet源码分析

    前面花了好几篇的篇幅把HashMap里里外外说了个遍,大家可能对于源码分析篇已经讳莫如深了.别慌别慌,这一篇来说说集合框架里最偷懒的一个家伙——HashSet,为什么说它是最偷懒的呢,先留个悬念,看完 ...

随机推荐

  1. 洛谷 [P1154] 奶牛分厩

    类似筛法的思想 本题实际上就是反推hash的模数, 首先想到枚举k,但显然会超时. $a mod k==b mod k <==> k|(a-b) $ 由同余的定义可以知道 所以我们的任务就 ...

  2. Sql2012数据库还原

    Sql2012数据库还原(通过.bak数据库备份文件) 昨天系统挂了,那叫一个悲惨,重装了系统,但是sql2012的数据没有备份,同事帮忙发来备份文件(.bak),开始还原数据. 步骤:1 自己新建一 ...

  3. 引用MinGW生成的.dll.a后出现的问题

    以前很少调用MinGW的运行时库,现在用到一个项目,用到了glib和gettext等. 遇到了一个问题,折腾了一个下午. gettext的运行时库之一是intl,MinGW只提供了.dll.a,于是参 ...

  4. python实现HOG+SVM对CIFAR-10数据集分类(上)

    本博客只用于学习,如果有错误的地方,恳请指正,如需转载请注明出处. 看机器学习也是有一段时间了,这两天终于勇敢地踏出了第一步,实现了HOG+SVM对图片分类,具体代码可以在github上下载,http ...

  5. 对JavaScript中的静态属性和原型属性的理解

    首先是在访问上的区别,当访问实例对象的某个属性但它本身没有时,它就会到原型中去查找,但不会去查找静态属性. // 实例对象不会去查找静态属性 function Foo(){} Foo.a = 1; v ...

  6. Animation Physics and The Realization Of Animation In Browsers

    Animation Physics Background With the development of computer science technology, people are facing ...

  7. LeetCode - 728. Self Dividing Numbers

    A self-dividing number is a number that is divisible by every digit it contains. For example, 128 is ...

  8. docker dead but pid file exists

    CentOS 6安装docker 报docker dead but pid file exists 执行 yum install epel-release yum install docker-io ...

  9. OpenCV角点检测goodFeaturesToTrack()源代码分析

    上面一篇博客分析了HARRIS和ShiTomasi角点检测的源代码.而为了提取更准确的角点,OpenCV中提供了goodFeaturesToTrack()这个API函数,来获取更加准确的角点位置.这篇 ...

  10. NOIP2017滚粗记

    NOIP2017滚粗记 扯淡 考完联赛后一直在搞文化... 联赛过去了不知道多少天了才来写这东西.... Day0 早自习知道了要期中考试. 感觉心态炸裂了. 上午在乱敲板子.... 打了一堆莫名其妙 ...