深入理解Java泛型
来源:互联网 发布:金蝶软件北京分公司 编辑:程序博客网 时间:2024/05/17 04:49
泛型引入
Java是一种强类型的语言,定义一个变量时需要指明其类型。Object是所有类的基类,在Java 1.5之前,为了让类具有通用性,通常使用Object来实现参数的任意化,如将String、Double等存储为Object类型,这个过程叫做自动装箱或向上转型。但是问题在于取数据时,必须做强制类型转换,将Object向下转型为String或Double等类型。向下转型存在很大的风险,需要事先知道具体的下转型类型是什么,一旦忘记或写错,在运行必然抛出异常,但在编译期间却不易发现,因此存在极大的安全隐患,应该尽量避免使用向下转型。
下面是使用Object的一个示例:
public class ObjectFoo { private Object foo; public ObjectFoo(Object f) { this.foo = f; } public Object getFoo() { return foo; } public void setFoo(Object foo) { this.foo = foo; }}
具体调用如下:
ObjectFoo strFoo1 = new ObjectFoo("hello Object");ObjectFoo intFoo1 = new ObjectFoo(100);// 此处需要强制类型转换为StringString strRst = (String) strFoo1.getFoo();// 此处需要强制类型转换为intint intRst = (int) intFoo1.getFoo();System.out.println("strFoo1.getFoo:" + strRst);System.out.println("intFoo1.getFoo:" + intRst);
正因为这个过程麻烦且容易出错,在Java 1.5中推出了泛型这个新特性,其本质是参数化类型。泛型在使用时会指明具体的类型,因此不需要向下转型,编译的时候也会检查类型安全,泛型还可以提高代码重用性,概括来说就是简单易用并且安全,泛型可用在类、接口、方法中,分别叫做泛型类、泛型接口和泛型方法,下面会一一介绍。
泛型类
依然以上述场景为例,先看一下利用泛型类是如何实现的:
public class GenericsFoo<T> { private T foo; public GenericsFoo(T f) { this.foo = f; } public T getFoo() { return foo; } public void setFoo(T foo) { this.foo = foo; }}
泛型类的调用:
// 使用泛型时必须指定具体类型,如String,不再需要类型转换GenericsFoo<String> strFoo2 = new GenericsFoo<>("Hello Generics");// 使用泛型时必须指定具体类型,如Integer,不再需要类型转换GenericsFoo<Integer> intFoo2 = new GenericsFoo<>(100);System.out.println("strFoo2.getFoo:" + strFoo2.getFoo());System.out.println("intFoo2.getFoo:" + intFoo2.getFoo());
从上面代码可以看到,跟普通的类相比,多出了<T>
符号,T是自定义的标识符,也作为参数,用来传递数据类型,称之为类型参数。T可以理解为数据类型的占位符,在运行时会被替换为真正的数据类型,上面示例中T会被替换为String和Integer。
泛型规则
- 泛型类型参数只能是类类型,包括自定义类。
- 类型参数不一定写为T,实际上可以任意定义。但习惯上使用单个大写字母,并且通常有如下含义:T-Type(表示一般数据类型)、K-Key(表示键)、V-Value(表示值)、N-Number(表示数值类型)、E-Element(集合中的元素,在集合中使用)、?(表示不确定的类型,用作通配符)等等。
- 泛型的类型参数可以有多个,用逗号隔开,如
<K, V>
。 - 泛型的参数类型可以使用extends语句,例如
<T extends superclass>
,叫做限制泛型。如果有多个类和接口,写法为<T extends SomeClass & interface1 & interface2 & interface3>
,但只能存在一个类,因为Java只能继承一个类,多个接口需写在类的后面。 - 泛型的参数类型可以是通配符类型,例如
Class<?> classType = Class.forName("java.lang.String")
。
泛型方法
使用泛型方法时不必指明参数类型,编译器会根据传递的参数自动查找具体的类型,这一点与泛型类不同。泛型方法与其所在类是不是泛型类没有关系,普通类也可拥有泛型方法,要定义一个泛型方法,只需要将类型参数放在修饰符之后、返回值之前即可。一旦定义了类型参数,就可以在参数列表、方法体及返回值中使用了。
下面代码是在上述泛型类的基础上增加了一个泛型方法:
public class GenericsFoo<T> { private T foo; public GenericsFoo(T f) { this.foo = f; } public T getFoo() { return foo; } public void setFoo(T foo) { this.foo = foo; } // 泛型方法 public <V> void getValue(V f) { System.out.println("The value is:" + f); }}
泛型方法除了定义不同,调用方法就跟普通方法一样:
GenericsFoo<String> strFoo2 = new GenericsFoo<>("Hello");strFoo2.getValue(strFoo2.getFoo());strFoo2.getValue("Generics Method");strFoo2.getValue(255);
输出结果:
The value is:Hello
The value is:Generics Method
The value is:255
泛型接口
泛型接口跟泛型类定义很像,直接放上示例代码,相信大家都能看懂。
定义泛型接口:
public interface Info<T> { public T getVar();}
泛型接口的实现类:
public class InfoImp<T> implements Info<T> { private T var; public InfoImp(T var) { this.var = var; } public void setVar(T var) { this.var = var; } @Override public T getVar() { return var; }}
调用示例:
public class GenericsInterface { public static void main(String[] args) { Info<String> strObj = new InfoImp<>("Hello Generics Interface"); System.out.println("The string value is:" + strObj.getVar()); Info<Integer> intObj = new InfoImp<>(1024); System.out.println("The integer value is:" + intObj.getVar()); }}
输出结果:
The string value is:Hello Generics Interface
The integer value is:1024
限制泛型
上面泛型类的示例中public class GenericsFoo<T>
并没有限制类型参数T的类型,实际上这里相当于Object,可以是任何类型。有时候我们需要对此处的类型做特定限制,这就是限制泛型了,听起来有点抽象,举个例子很容易就明白了。
限制泛型的基本语法为<T extends SuperClass(或Interface)>
,这里的extends需要广义理解,接口也是用它,这就表示T的类型只能是该SuperClass的子类或者是任何实现了这个接口的类的类型,这样就对泛型做了限制。
假设定义一个父类Geometry:
public class Geometry { public String name; public double area;}
再定义3个类,其中Circle、Square继承自Geometry,Keyboard没有继承Geometry。
public class Circle extends Geometry {}
public class Square extends Geometry {}
public class Keyboard {}
限制泛型类的定义如下:
public class GenericsRestrict<T extends Geometry> {}
如上所示,泛型类型仅能是Geomerty或其子类,其他类型则会报错。
调用测试,如果是限制之外的类型就会报错:
public class TestClass { public static void main(String[] args) { GenericsRestrict<Square> square = new GenericsRestrict<>(); GenericsRestrict<Circle> circle = new GenericsRestrict<>(); // 因为Keyboard没有继承Geometry,下面语句报错 GenericsRestrict<Keyboard> keyboard = new GenericsRestrict<>(); }}
当然extends后面可以是接口或类与多个接口,规范及原理相同,这里不再赘述。
通配符泛型
为了解决类型被限制死了而不能动态根据需要来确定的缺点,引入了“通配符泛型”, 如<? extends Collection>
,?代表未知类型,这个类型可以是Collection接口的所有实现类。使用通配符有以下两点需要注意:
- 如果只指定了
<?>
,而没有extends,则可以是任意类型。 - 使用通配符?不但可以限制类型的上限,还可以限制下限。限制下限使用 super 关键字,例如
<? super Number>
表示只能接受 Number 及其父类,可接受的最低类型为Number。上限限制则是使用上面提到的extends关键字。
注意
如果在使用泛型时没有指定具体的数据类型,就会擦除泛型类型,并向上转型为 Object,在获取数据时必须向下强制类型转换,这与不使用泛型是一样的。
- 深入理解Java泛型
- 深入理解java泛型
- 深入理解java泛型
- 深入理解JAVA泛型
- 深入理解Java泛型
- 深入理解 Java 泛型
- 深入理解 Java 泛型
- 深入理解java泛型
- 深入理解java泛型
- 深入理解java数组
- 深入理解java多态性
- 深入理解java String
- 深入理解java多态性
- 深入理解java多态性
- 深入理解Java多态性
- 深入理解Java多态性
- 深入理解Java多态性
- 深入理解java多态性
- maven基础
- 【杭电】1106-排序
- [李景山php]thinkphp核心源码注释|Mysql.class.php
- UIView的setNeedsDisplay函数和setNeedsLayout函数的调用情况
- 个人收藏,查询局域网电脑的IP,端口号,MAC地址(黑客技术入门)
- 深入理解Java泛型
- MFC 下开发Office Word2010报表生成方法-- 一、准备工作
- 代码评审出现的简单问题
- PHP图片上传程序(完整版)
- [李景山php]thinkphp核心源码注释|Oracle.class.php
- 指针及其应用(一)
- laravel 初见 安装 路由 模块化 2016.07.20回顾
- [李景山php]thinkphp核心源码注释|Pgsql.class.php
- [李景山php]thinkphp核心源码注释|Sqlite.class.php