目录

  1、导读
  2、为何需要泛型?
   3、泛型的定义格式
   3、泛型的好处
  4、什么时候使用泛型?
   5、泛型的擦除
   6、泛型的补偿
  7、泛型的应用
      7.1【泛型类】
     7.2【泛型方法】
     7.3【泛型接口】
  8、泛型の通配符:?
  9、泛型的限定

1、导读

泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。Generic有“类的,属性的”之意,在Java中代表泛型。泛型作为一种安全机制而产生。

2、为何需要泛型?

我们知道集合(Collection、Map之类的容器)是用来存储任意对象(Object)的一系列“容器类或者接口”,注意这里的“任意对象”,就是指我们可以在这些类或接口中存放任意类型的对象,但这些对象在存储之前都需要统一向上转型为Object类型(因为Object类是所以类的的父类),比如类ArrayList就属于“集合类”,我们可以用这个类来存储任意对象:

  1. package bean;
  2.  
  3. import java.util.ArrayList;
  4.  
  5. public class ArrayListDemo {
  6. public static void main(String[] args) {
  7.  
  8. //首先我们创建一个ArrayList对象
  9. ArrayList al = new ArrayList();
  10.  
  11. //往"容器"添加元素:我们添加了字符串"哈哈",数字2,boolean类型true,最后我们甚至添加了自己。
  12. al.add("哈哈");
  13. al.add(2);
  14. al.add(true);
  15. al.addAll(al);
  16. //由于ArrayList类覆盖了toString(),这里我们可以直接打印查看效果:
  17. System.out.println(al);
  18. }
  19. }

最后打印出来的字符串是:[哈哈, 2, true, 哈哈, 2, true]

值得注意的是:这里的2不是int而是Integer,因为ArrayList只能存对象,而“添加它自己”addAll(al)的实质是将al中的所有元素再存入al中,正如打印效果显示的一样,我们成功添加了类型不一样的对象:有String,有Integer、还有Boolean。当然,你可以添加任意你想要添加的对象。因为这些对象在存入“容器”al中时会全部转型为Object类型,有没有觉得这样的“容器”用起来会很爽,什么东西都能往里放。但是,便捷往往会付出相应代价。

  我们将各种类型的元素存入“容器”中当然不是让它睡上一觉,我们需要将存储的元素取出使用。对于这些“容器”,我们一般使用迭代器Iterator来获取每一个元素。(这里不进行迭代器使用说明,只需理解迭代器是取出"容器"中元素的工具就行).

假设现在我们需要对我们上面创建的“容器”al中的元素进行操作:打印容器中字符串的长度,即“哈哈”的长度。

  1. 1 package bean;
  2. 2
  3. 3 import java.util.ArrayList;
  4. 4 import java.util.Iterator;
  5. 5
  6. 6 public class ArrayListDemo {
  7. 7 public static void main(String[] args) {
  8. 8 //首先我们创建一个ArrayList对象,并添加对象
  9. 9 ArrayList al = new ArrayList();
  10. 10 al.add("哈哈");
  11. 11 al.add(2);
  12. 12 al.add(true);
  13. 13
  14. 14 //使用迭代器Iterator进行遍历,并打印容器中字符串的长度:
  15. 15 //注:next()方法是按照容器的存储顺序,逐一取出第一个到最后一个对象。
  16. 16 Iterator i = al.iterator();
  17. 17 while(i.hasNext()){
  18. 18 String s = (String) i.next();
  19. 19 System.out.println(s.length());
  20. 20 }
  21. 21 }
  22. 22
  23. 23 }

这是运行结果:

我们来进行代码分析:while循环中,先用i.next()方法取出第一个元素"哈哈",然后装换为String类型(因为存进去的时候是Object类型,使用的使用要向下转型),所以接下来打印了“哈哈”的长度=2。接着循环:迭代器取出第二个元素:2,但这是Integer类型不能强制装换为String类型,所以抛出异常:ClassCastException:类型转换异常。

这就是“能存万物”所带来的代价:我们在使用“容器”存储对象并调用时就换存在“类型转换异常”的安全隐患,但这需要在运行后才能得知,编译时无法发现。所以我们该如何解决这个问题呢?我们发现问题在于“类型转换异常”,所以我们只要保证“容器”中存储的是同一类型的元素就不会出现这个异常了。类似数组的定义一样:如 int[] 就表示专门用来存储int类型数据的数组,String[] 就是用来存储String类型数据的数组。我们只要将“容器”标上相应类型的标签就可以了。看到这里,你应该已经理解了泛型的由来,这就是泛型的设计:引入类型参数的概念,即对对象类型进行申明。

3、泛型的定义格式

格式为:类名<类型名>,如:

  

  1. 1 package bean;
  2. 2
  3. 3 import java.util.ArrayList;
  4. 4 import java.util.Iterator;
  5. 5
  6. 6 public class ArrayListDemo {
  7. 7 public static void main(String[] args) {
  8. 8 //首先我们创建一个ArrayList对象,并申明泛型类型为String类型:只能存储String类型的对象
  9. 9 ArrayList<String> al = new ArrayList<String>();
  10. 10 al.add("哈哈");
  11. 11 al.add("asdafa");
  12. 12 al.add("tobeyourself");
  13. 13
  14. 14 //使用迭代器Iterator进行遍历,并打印容器中字符串的长度:这里迭代器也使用了泛型
  15. 15 Iterator<String> i = al.iterator();
  16. 16 while(i.hasNext()){
  17. 17 String s = (String) i.next();
  18. 18 System.out.println(s+"的长度为="+s.length());
  19. 19 }
  20. 20 }
  21. 21
  22. 22 }

运行结果为:

3、泛型的好处

除了解决上面的安全隐患即抛出异常问题,泛型的类或接口在取出对象时将不需要再进行向下类型转换,因为存储的时候就是该类型 。

另外,泛型的使用让安全问题在编译时就报错而不是运行后抛出异常,这样便于程序员及时准确地发现问题。

4、什么时候使用泛型?

一般类或接口在api文档中的定义中带有<E>标识的,在使用的时候都需要使用泛型机制。如ArrayList<E>、Iterator<E>。

5、泛型的擦除

泛型是运用在编译时期的技术:编译时编译器会按照<类型名>的类型对容器中的元素进行检查,检查不匹配,就编译失败。如果全部检查成功,则编译通过,但,编译通过后产生的.class文件中并没有<类型名>这个标识,即类文件中没有泛型,这就是泛型的擦除。

一句话总结就是:在.java文件运用泛型技术时,编译器在文件编译通过后自动擦除泛型标识。

由于泛型的擦除,类文件中没有泛型机制,同时也没有使用向下类型转换,那么为何运行时无异常?

6、泛型的补偿

编译器在擦除泛型后,会自动将类型转换为原定义的"泛型",这样就不必再做向下类型转换了。

泛型的擦除和补偿 这两个机制都是编译器内部自动完成的,了解其原理即可。

7、泛型的应用

7.1【泛型类】

所谓的泛型类就是运用到泛型技术的类,如上面讲到的ArrayList<E>,Iterator<E>等都是java中的泛型类,这些类都是Java已经定义好的泛型类,直接使用就可以了。但有时候我们会遇到这样的问题:

假设我们现在有两个自定义类:Worker类和Student类,现在我们需要一个工具类Tool来获取Worker对象和Student对象,并能对对象进行操作。

    分析:

我们可能会想到将Worker和Student类作为Tool类的成员变量,以此来实现对这两个类的操作。但这样有一个问题,就是如果这不是两个类而是很多类,甚至说无数个类,即Tool类可以操作任意类,这种通过添加成员变量来实现调用对象的方法显然不可行。注意这个“添加任意对象”,这不就是上面说的ArrrayList<E>这些类所具有的特点吗?泛型类就是为了解决“添加任意对象”而产生的,这里提到的ArrayList<E>属于已定义泛型类(Java中自带的),这里我们要用到Tool类来“存储任意对象”,所以要将Tool类定义为泛型类,这就是根据需求自己设计的自定义泛型类:

首先我们先简单的定义一下:Worker类和Student类:

Worker类:

  1. 1 package bean;
  2. 2
  3. 3 public class Worker {
  4. 4 private String name;
  5. 5 private int age;
  6. 6 Worker(){
  7. 7
  8. 8 }
  9. 9 Worker(String name,int age){
  10. 10 this.name = name;
  11. 11 this.age = age;
  12. 12 }
  13. 13 public String getName() {
  14. 14 return name;
  15. 15 }
  16. 16 public void setName(String name) {
  17. 17 this.name = name;
  18. 18 }
  19. 19 public int getAge() {
  20. 20 return age;
  21. 21 }
  22. 22 public void setAge(int age) {
  23. 23 this.age = age;
  24. 24 }
  25. 25
  26. 26 }

Student类:

  1. 1 package bean;
  2. 2
  3. 3 public class Student {
  4. 4 private String name;
  5. 5 private String sex;
  6. 6 Student(){
  7. 7
  8. 8 }
  9. 9 Student(String name,String sex){
  10. 10 this.name = name;
  11. 11 this.sex = sex;
  12. 12 }
  13. 13 public String getName() {
  14. 14 return name;
  15. 15 }
  16. 16 public void setName(String name) {
  17. 17 this.name = name;
  18. 18 }
  19. 19 public String getSex() {
  20. 20 return sex;
  21. 21 }
  22. 22 public void setSex(String sex) {
  23. 23 this.sex = sex;
  24. 24 }
  25. 25
  26. 26 }

现在我们需要将Tool类定义为泛型类:(注意格式)

  1. 1 package bean;
  2. 2
  3. 3 public class Tool<E> {
  4. 4 private E e;
  5. 5
  6. 6 public Tool(E e1){
  7. 7 this.e = e1;
  8. 8 }
  9. 9
  10. 10 public E getE() {
  11. 11 return e;
  12. 12 }
  13. 13 public void setE(E e) {
  14. 14 this.e = e;
  15. 15 }
  16. 16
  17. 17 }

这里我们写的很简单,但已经满足需求了,重点在于泛型类的写法:Tool<E>,这里的E字母可以写你自己喜欢的代码,这也就是类型参数的应用,E相当于一个类型参数,代表了Tool<E>可以传入任意对象,下面我们具体使用来看看效果:

  1. 1 package bean;
  2. 2
  3. 3 class Tooltest{
  4. 4 public static void main(String[] args) {
  5. 5 //假设有两个对象:一个学生,一个工人
  6. 6 Student t1 = new Student("张三","男");
  7. 7 Worker w1 = new Worker("李四",20);
  8. 8
  9. 9 //现在我们使用Tool类来调用t1和w1
  10. 10 Tool<Student> ts = new Tool<Student>(t1);
  11. 11 Tool<Worker> tw = new Tool<Worker>(w1);
  12. 12
  13. 13 //打印查看效果
  14. 14 System.out.println("使用ts调用t1中的数据:"+ts.getE().getName()+":"+ts.getE().getSex());
  15. 15 System.out.println("使用tw调用w1中的数据:"+tw.getE().getName()+":"+tw.getE().getAge());
  16. 16 }
  17. 17 }

打印结果为:

使用ts调用t1中的数据:张三:男
使用tw调用w1中的数据:李四:20

7.2【泛型方法】

泛型方法类似于静态类的设计,一般的方法传入的参数是固定类型的,如public void show(int i){}这个方法的参数类型固定为int,但是泛型方法可以传入指定的参数类型。一般泛型定义有两种形式:

1)使用泛型类的参数类型来定义(常用):

如泛型类Tool<QQ>,它的泛型参数即为QQ。那么泛型方法可以这样写:public void show(QQ qq){}.

2)使用自定义的参数类型来定义:

如果我们需要自定义参数类型,那么我们把泛型参数放在方法上就可以了(放在返回值类型之前):public <AA> void show(AA aa){}。

注:静态方法不能访问类的泛型,如果需要泛型,我们只能使用方法2),在方法上使用泛型即可:public static <AA> void show(AA aa){}

7.3【泛型接口】

和上面一样的道理,当我们不确定使用对象的类型时,运用泛型就可以解决问题,泛型接口和泛型类的使用是一样一样的。

我们只需要注意,在实现泛型接口时有两种情况:这里假设有泛型接口interf<AA>,它的实现类是Tool。

1)确定实现的泛型接口的参数类型:

假设Tool类需要String类型的参数,那么实现可以直接写:class Tool implements interf<String>{}即可。

2)不确定实现的泛型接口的参数类型:

这时候我们需要泛型类来实现classTool<BB> implements interf<BB>{}.

8、泛型の通配符:?

当我们不确定传入的对象类型时我们就可以使用?来代替。“?”即泛型通配符。

9、泛型的限定

我们知道使用泛型类时:如果明确参数类型,那么泛型就代表一种类型;如果使用通配符?,那么泛型就代表任意类型。但有时候我们希望指定某些类型(不是一个,也不要所有)能作为参数类型,这应该怎么办呢?

Java中利用泛型的限定解决了这个问题,即泛型的限定。我们只需要按这样的格式书写:

上限:<? extends E>表示参数类型是E及其所有子类。

下限:<? super E>表示参数类型是E及其所有超类(即父类)。

https://www.cnblogs.com/fzz9/articles/7674561.html

作者:风之之

Java泛型全解析【接口、类、封装类型】的更多相关文章

  1. java最全的验证类封装

    package com.tongrong.utils; import java.util.Collection; import java.util.Map; import java.util.rege ...

  2. Java 泛型示例 - 泛型方法,类,接口

    Java Genrics 是 Java 5 中引入的最重要的功能之一. 如果您一直在使用Java Collections并使用版本 5 或更高版本,那么我确定您已经使用过它. Java 中具有集合类的 ...

  3. 《Java面试全解析》505道面试题详解

    <Java面试全解析>是我在 GitChat 发布的一门电子书,全书总共有 15 万字和 505 道 Java 面试题解析,目前来说应该是最实用和最全的 Java 面试题解析了. 我本人是 ...

  4. 《Java面试全解析》1000道面试题大全详解(转)

    <Java面试全解析>1000道 面试题大全详解 本人是 2009 年参加编程工作的,一路上在技术公司摸爬滚打,前几年一直在上海,待过的公司有 360 和游久游戏,因为自己家庭的原因,放弃 ...

  5. java基础2.-------interface接口类,实现接口

    1.为什么使用接口,是把功能方法都写在一个类中,在其他需要调用的时候,通过implements实现该接口 2.接口可以继承多个父类接口,在实现接口的时候,实现类实现所有方法 3.在接口类写方法时,自动 ...

  6. Java泛型、List接口整理

    泛型 package com.oracle.demo01; import java.util.HashMap; import java.util.Iterator; import java.util. ...

  7. Java泛型中的类型参数和通配符类型

    类型参数 泛型有三种实现方式,分别是泛型接口.泛型类.泛型方法,下面通过泛型方法来介绍什么是类型参数. 泛型方法声明方式:访问修饰符 <T,K,S...> 返回类型 方法名(方法参数){方 ...

  8. Java反射全解析(使用、原理、问题、在Android中的应用)

    前言 今天说Java模块内容:反射. 反射介绍 正常情况下,我们知晓我们要操作的类和对象是什么,可以直接操作这些对象中的变量和方法,比如一个User类: User user=new User(); u ...

  9. Java IO 流--FileUtils 工具类封装

    IO流的操作写多了,会发现都已一样的套路,为了使用方便我们可以模拟commosIo 封装一下自己的FileUtils 工具类: 1.封装文件拷贝: 文件拷贝需要输入输出流对接,通过输入流读取数据,然后 ...

随机推荐

  1. ubuntu14.0.4安装kafka

    1. 下载 zookeeper-3.4.12 zookeeper download 2 配置Zookeeper 进入 zookeeper 的 conf 目录下,找到 zoo_sample.cfg 文件 ...

  2. 【JS新手教程】JS中的split()方法,拆分字符串

    该方法具体如代码和图中的注释,直接在语句后面加注释了.格式:要拆分的字符串.split(拆分依据的字符)例如该文中的例子,拆分人名,电话,地址.该文中用了个文本框,文本框中需要输入的格式是:人名,电话 ...

  3. SourceTree 免登录跳过初始设置的方法 for Windows

    SourceTree安装包下载地址: 链接: https://pan.baidu.com/s/1rOPQkfNqvLGcIeZNw0aAjw 密码: 42us nodev6.9.0 和git 链接:h ...

  4. jquery img src赋值

    不用Jquery时:document.getElementById("imageId").src = "xxxx.jpg"; 用Jquery时:$(" ...

  5. SecureCRT配色方案(转)

    1. 设置背景颜色和字体颜色: 选项(Options)==>会话选项(Sessions options)==>终端(Terminal)==>仿真(Emulation)  选项(Opt ...

  6. 开启Hadoop和Spark的学习之路

    Hadoop Hadoop是一个由Apache基金会所开发的分布式系统基础架构. 用户可以在不了解分布式底层细节的情况下,开发分布式程序.充分利用集群的威力进行高速运算和存储. Hadoop实现了一个 ...

  7. (简单实用)Android支付宝商家收款语音播报

    支付宝商家收款时,语音提示:支付宝收款xxx元,当时觉得这东西还挺有趣的,第一时间通知给商家,减少不必要的纠纷,节约时间成本,对商家对用户都挺好的. 在商家版有这样收款播报的功能,我觉得挺好的. 对列 ...

  8. php生成动态验证码 加减算法验证码 简单验证码

    预览效果: <?php /** *ImageCode 生成包含验证码的GIF图片的函数 *@param $string 字符串 *@param $width 宽度 *@param $height ...

  9. 03 python 对象笔记

    类的命名方法 1.使用大驼峰命名法:每一个单词的首字母大写(第一个的也要)2.单词之间不需要下划线 对象的内置函数和属性 1.使用dir()函数来获取对象的内置方法和属性.返回值是一个列表.2.返回中 ...

  10. 最佳thread数量

    cpu 密集性task,过多的线程反而降低了处理效率,最佳的做法就是保持和cpu core数量大致相同的线程数量: threads = number of CPUs + 1 io密集型,因为会有cpu ...