【Java入门提高篇】Day14 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语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

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

  欢迎大家继续关注!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Crossin的编程教室

【Python 第52课】 元组

上一次pygame的课中有这样一行代码: x, y = pygame.mouse.get_pos() 这个函数返回的其实是一个“元组”,今天我们来讲讲这个东西。...

34570
来自专栏全沾开发(huā)

柯里化与反柯里化

柯里化与反柯里化 最近在看一本书《JavaScript函数式编程》 里边提到了一个名词,柯里化(currying),阅读后发现在日常...

424110
来自专栏Java Web

Java学习笔记(2)——数据类型

终于要写点干货了,其实思考了很久下面一篇文章要写什么,主要的纠结点在于,既想要分享那些精美的知识,又怕这些知识不太好嚼。后来想想还是对初学者不太好友算了..一...

28030
来自专栏数说工作室

统计师的Python日记【第1天:谁来给我讲讲Python?】

统计师的Python日记 【第一天】谁来给我讲讲Python? 我是一名数据分析师,曾在漫长的岁月中使用SAS、Matlab和R(使用频率依次递减)。其他如...

47670
来自专栏编程

Python入门基础连载(2)数据结构

Python数据结构包括了列表(list),元组(tuple),字典(dict)和集合(set),这些也都可以称之为容器,下面Cooldog就和大家一起学习一下...

20770
来自专栏和蔼的张星的图像处理专栏

55. 比较字符串

比较两个字符串A和B,确定A中是否包含B中所有的字符。字符串A和B中的字符都是 大写字母 样例 给出 A = "ABCD" B = "ACD",返回 tru...

14910
来自专栏Golang语言社区

interface引发的事件真相

流动的水没有形状,漂流的风找不到踪迹,一切代码都了然于心,我们在写代码的时候,总是有一种思维定式陪伴左右,在对事物做判断的时候,往往这种思维定式会往正向或反向做...

34860
来自专栏zhisheng

干货分享:让你分分钟学会 javascript 闭包 一像素

闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它。...

35450
来自专栏用户2442861的专栏

python 中迭代多个序列

http://blog.csdn.net/he_jian1/article/details/40819407

7020
来自专栏互联网开发者交流社区

面向对象的Java实现

1-1:封装 a.为什么需要封装(封装可以是数据方便维护、增加实用性、方便扩展等等。通过面向对象的思想,模拟现实生活中的事物。) b.什么是封装(封装就是将...

9510

扫码关注云+社区

领取腾讯云代金券