介绍

用法:

List list = new ArrayList();// 1
list .add(new Integer(12));// 2
Integer x = (Integer) list .iterator().next();// 3// 第3 行的类型转换有些烦人,为了保证对Integer 类型变量赋值的类型安全,必须进行类型转换。
//当然,因为程序员可能不清楚他们的类型,导致这个类型转换有可能产生一个运行时错误。
//而如何把一个list(集合) 中的内容限制为一个特定的数据类型呢?
//这就是generics背后的核心思想。这是上面程序片断的一个泛型版本:List<Integer> list = new ArrayList<Integer>(); // 1
list.add(new Integer(12)); // 2
Integer x = list.iterator().next(); // 3//注意第1行变量list的类型声明。
//它指定这不是一个任意的List,而是一个Integer 的List。
//我们说List是一个带一个类型参数的泛型接口,我们在创建这个List 对象的时候指定了一个Integer类型参数是。
//另一个需要注意的是第3行没了类型转换。

现在,我们用第1行的类型参数取代了第3 行的类型转换。然而,这里还有个很大的不同。编译器现在能够在编译时检查程序的正确性。当我们说list 被声明为ist<Integer>类型,这告诉我们无论何时何地使用list 变量,编译器保证其中的元素的正确的类型。实际结果是,这可以增加可读性和稳定性,尤其在大型的程序中。

泛型的设计背景

Java中的泛型是什么 ?

所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。

集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,在集合中存储对象并在使用前进行类型转换是很不方便。

JDK1.5之后使用泛型来解决。这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection<E>,List<E>,ArrayList<E>这个<E>就是类型参数,即泛型。允许我们在创建集合时再指定集合元素的类型,正如:List<String>,这表明该List只能保存字符串类型的对象。

JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参。

使用泛型的好处是什么?

它提供了编译期的类型安全,确保你只能把正确类型的对象放入集合中,避免了在运行时出现ClassCastException。

在集合中没有泛型时任何类型都能够加入集合中,类型不安全,读出来的时候还需要强转。

在集合中有泛型时只有指定类型才能添加到集合中,类型是安全的,读出来的时候不需要强转,很便捷。

自定义泛型结构

泛型的声明

Interface List<T> 和class GenTest<K,V>
//其中,T,K,V,E不代表值,而是表示类型。这里使用任意字母都可以。
//常用T表示,是Type的缩写。

泛型的实例化:

一定要在类名后面指定类型参数的值(类型)。如:   

List<String> strList= new ArrayList<String>();
Iterator<Customer> iterator = customers.iterator();

T只能是类,不能用基本数据类型填充。但可以使用包装类填充,把一个集合中的内容限制为一个特定的数据类型,这就是generics背后的核心思想

使用泛型的主要优点是能够在编译时而不是在运行时检测错误。

自定义泛型类, 泛型接口

泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>

实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。

泛型不同的引用不能相互赋值。泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。经验:泛型要使用一路都用。要不用,一路都不要用。

如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。泛型的指定中不能使用基本数据类型,可以使用包装类替换。

在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。

异常类不能是泛型的

父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:

子类不保留父类的泛型:按需实现

没有类型擦除

具体类型

子类保留父类的泛型:泛型子类

全部保留

部分保留

子类除了指定或保留父类的泛型,还可以增加自己的泛型

自定义泛型方法

方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。

/*** 泛型方法的格式* [访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称])抛出的异常* 泛型方法声明泛型时也可以指定上限**/public class DAO {public<E> E get(intid, E e) {E result= null;return result;}}

泛型和子类继承

让我们测试一下我们对泛型的理解。下面的代码片断合法么?

List<String> ls = new ArrayList<String>(); //1
List<Object> lo = ls; //2

第1 行当然合法,但是这个问题处在于第2 行。这产生一个问题:一个String 的List 是一个Object 的List 么?大多数人的直觉是回答: “当然!”。因为乍看起来String是一种Object,所以List<String>应当可以用在需要List<Object>的地方,但是事实并非如此。真这样做的话会导致编译错误。

好,在看下面的几行

lo.add(new Object()); // 3
String s = ls.get(0); // 4: 试图把Object 赋值给String

这里,我们使用lo 指向ls。我们通过lo 来访问ls,一个String 的list。我们可以插入任意对象进去。结果是ls 中保存的不再是String。当我们试图从中取出元素的时候,会得到意外的结果。java 编译器当然会阻止这种情况的发生。第2 行会导致一个编译错误。总之,如果Foo 是Bar 的一个子类型(子类或者子接口),而G 是某种泛型声明,那么G<Foo>是G<Bar>的子类型并不成立!!

如果你再深一步考虑,你会发现Java这样做是有意义的,因为List<Object>可以存储任何类型的对象包括String, Integer等等,而List<String>却只能用来存储Strings。 

通配符(Wildcards)

考虑写一个例程来打印一个集合(Collection)中的所有元素。下面是在老的语言中你可能写的代码:

void printCollection(Collection c) {Iterator i = c.iterator();for (int k = 0; k < c.size(); k++) {System.out.println(i.next());}
}下面是一个使用泛型的幼稚的尝试(使用了新的循环语法):void printCollection(Collection<Object> c) {for (Object e : c) {System.out.println(e);}
}

一个集合,它的元素类型可以匹配任何类型。显然,它被称为通配符。我们可以写:

void printCollection(Collection<?> c) {for (Object e : c) {System.out.println(e);}
}

现在,我们可以使用任何类型的collection 来调用它。注意,我们仍然可以读取c 中的元素,其类型是Object。这永远是安全的,因为不管collection 的真实类型是什么,它包含的都是Object。但是将任意元素加入到其中不是类型安全的

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // 编译时错误

         因为我们不知道c 的元素类型,不能添加对象。add 方法有类型参数E 作为集合的元素类型。我们传给add 的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。唯一的例外是null,它是所有类型的成员。另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object

有限制的通配符(Bounded Wildcards)

限定通配符和非限定通配符。List<? extends T>和List <? super T>这两个List的声明都是限定通配符的例子:

List<? extends T>可以接受任何继承自T的类型的List.

List<? super T>可以接受任何T的父类构成的List。例如List<? extends Number>可以接受List<Integer>或List<Float>        

泛型方法

        考虑写一个方法,它用一个Object 的数组和一个collection 作为参数,完成把数组中所有object 放入collection 中的功能。下面是第一次尝试:

static void fromArrayToCollection(Object[] a, Collection<?> c) {for (Object o : a) {c.add(o); // 编译期错误}
}

       把对象放进一个未知类型的集合中。办法是使用generic methods就像类型声明,方法的声明也可以被泛型化——就是说,带有一个或者多个类型参数

static <T> void fromArrayToCollection(T[] a, Collection<T> c){for (T o : a) {c.add(o); // correct}
}

我们可以使用任意集合来调用这个方法,只要其元素的类型是数组的元素类型的父类。

        Object[] oa = new Object[100];Collection<Object> co = new ArrayList<Object>();fromArrayToCollection(oa, co);// T 指ObjectString[] sa = new String[100];Collection<String> cs = new ArrayList<String>();fromArrayToCollection(sa, cs);// T inferred to be StringfromArrayToCollection(sa, co);// T inferred to be ObjectInteger[] ia = new Integer[100];Float[] fa = new Float[100];Number[] na = new Number[100];Collection<Number> cn = new ArrayList<Number>();fromArrayToCollection(ia, cn);// T inferred to be NumberfromArrayToCollection(fa, cn);// T inferred to be NumberfromArrayToCollection(na, cn);// T inferred to be NumberfromArrayToCollection(na, co);// T inferred to be ObjectfromArrayToCollection(na, cs);// compile-time error}static <T> void fromArrayToCollection (T[] a, Collection<T> c) {for (T o : a) {c.add(o); // correct}}

        注意,我们并没有传送真实类型参数(actual type argument)给一个泛型方法。编译器根据实参为我们推断类型参数的值。它通常推断出能使调用类型正确的最明确的类型参数。

java基础知识(七)-- 泛型(Generics )相关推荐

  1. Java基础知识(七) 输入输出流

    Java基础知识 输入输出流 1. Java IO流的实现机制是什么 2. 管理文件和目录的类是什么 3. Java Socket是什么 4. Java NIO是什么 5. 什么是Java序列化 6. ...

  2. Java基础知识之泛型全接触

    当我们在定义类,接口和方法时,可以接收一个类型作为参数,这就叫做泛型. 函数可以传入普通的参数,也可以传入一个类型参数.不同之处是普通的参数就是值而已,但是类型参数却是个类型. 使用泛型的好处: 强类 ...

  3. Java基础知识(八) Java平台与内存管理

    Java基础知识 1. 为什么说Java是平台独立性语言 2. Java 平台与此他语言平台有哪些区别 3. JVM 加载 class 文件的原理机制是什么 4. 什么是GC 5. Java 是否存在 ...

  4. Java基础知识(二)(Object类的常用方法、日期时间类、System类、StringBuilder类、包装类、Collection集合、Iterator迭代器、泛型、list集Set接口...)

    文章目录 Java基础知识(二) 1.Object类的常用方法 1.1 toString方法 1.2 equals方法 1.3 Objects类 2.日期时间类 2.1 Date类 2.2 DateF ...

  5. java(5)---Java基础知识大全

    Java基础知识大全 一.基础知识:  1.JVM.JRE和JDK的区别:  JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性.  java语言是跨平 ...

  6. JAVA基础知识学习全覆盖

    文章目录 一.JAVA基础知识 1.一些基本概念 1.Stringbuffer 2.局部变量成员变量 3.反射机制 4.protect 5.pow(x,y) 6.final ,finally,fina ...

  7. Java基础知识(全面)

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章导航 前言 一.java基础语法 1.java语言介绍 2.三大平台 3.语言特点 4.Jvm.jre 和 jdk 5.环境变量 ...

  8. 【转】Java基础知识整理

    本博文内容参考相关博客以及<Java编程思想>整理而成,如有侵权,请联系博主. 转载请注明出处:http://www.cnblogs.com/BYRans/ PDF版下载链接:<Ja ...

  9. java基础知识之初识java

    java基础知识之初识java JAVA基础课后总结 一 1.计算机程序 定义:程序(Program)是为实现特定目标或解决特定问题而用计算机语言编写的命令序列的集合. 2.指令 定义:指令就是指示机 ...

  10. java 基础知识总结

    Java基础知识总结 写代码: 1,明确需求.我要做什么? 2,分析思路.我要怎么做?1,2,3. 3,确定步骤.每一个思路部分用到哪些语句,方法,和对象. 4,代码实现.用具体的java语言代码把思 ...

最新文章

  1. std::map只修改不用加锁
  2. “第三届中国行业互联网大会暨CIO班12周年年会”成功举行
  3. Android音频开发(4):如何存储和解析wav文件
  4. [解决]电信彩信网关开发错误-SOAP_VERSIONMISMATCH
  5. 基于三维点云场景的语义及实例分割:RandLA-Net和3D-BoNet
  6. git 强制将本地代码更新仓库里面的代码
  7. python import 类如何捕获clrt c_Python3 与 C# 扩展之~基础衍生
  8. python动态人脸识别_python3.8动态人脸识别
  9. 金融评分卡项目—6.互联网金融业贷款申请评分卡介绍
  10. python基础篇——列表与列表算法(上)
  11. 如何开启WIN10卓越性能模式
  12. vmware(鼠标移出移入)反复触发numlock问题
  13. 项目构建工具--webpack
  14. oracle中的declare
  15. 我们看到的太阳是8分钟前的太阳
  16. Vue3实现打字机效果
  17. itunes cannot read the contents of the iphone
  18. 复现笔记--有关极性码的两篇文章
  19. 嵌入式系统(五):RISC-V4
  20. 2021-5-10:Spring Boot整合MyBatis

热门文章

  1. 各向异性渲染(一)基础理论
  2. 你若安好便是晴天_百度百科
  3. android 滑屏功能,Android 滑屏效果实现方式汇总
  4. JS 无形装逼,最为致命
  5. MD 的图片格式排版偏差的问题
  6. np.array的axis进行横向纵向的求和运算
  7. 171025_matlab_imag函数
  8. 食物也疯狂!KOOCAN盘点因为食物毁掉的中国电视剧
  9. php的微信登录示例代码,关于微信用户注册登录实例代码汇总
  10. 微机—— 计算机中的数据表示