博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java动态编程之泛型
阅读量:7197 次
发布时间:2019-06-29

本文共 3772 字,大约阅读时间需要 12 分钟。

hot3.png

1. 泛型的概念

泛型本质上就是参数化类型(parameterized type)。在定义类、接口、方法的时候,把将要操作的数据类型声明为形参。在实例化的时候,再传入实际的数据类型,就是由类型实参指定真实数据类型。这就是泛型。泛型主要目的是复用算法。对于不同的数据类型,算法逻辑一样,就不用针对不同数据类型写不同的代码。可以增强代码的可扩展性,减少工作量。比如ArrayList,可以保存任何数据类型,不需要针对每个数据类型写一个ArrayList类。

在Java 5之前,没有泛型。在这之前,复用算法的主要手段是使用超类。比如可以将ArrayList里面的数据类型定义为Object,这样就可以保存任何数据类型了。但是,这样容许使用者在同一个ArrayList保存不同的数据类型,在代码进行强制类型转换的时候,就会发生类型错误。泛型可以保证类型安全,因为它可以让强制类型转换自动地、隐式地进行。

但是,因为Java 5之前没有泛型,所以泛型需要兼容以前非泛型的代码。就是非泛型代码必须能处理泛型,泛型代码也必须能处理非泛型代码。Java是使用擦除特性(erasure)来实现的。当Java代码编译时,所有泛型类型信息都将被删除(擦除)。也就是说,当java文件编译成class文件后,会使用类型形参的约束类型来替换类型形参。举个例子,ArrayList<E>中保存的元素,在class文件看来就是Object。然后使用隐式地强制转换来与类型实参指定的类型保持兼容。

总结一下就是:泛型扩展了复用算法的能力,并且可以保证类型安全。通过编译后类型擦除和隐式强制转换,保证和以前的非泛型代码的兼容性。

2. 对类型形参进行约束

有些情况下,泛型类型可以没任何约束,也就是任何Object都可以。但是在另外一些情况下,算法只和特定的数据类型有关。比如你希望对任何数值(包括整数,浮点数和双精度数)执行计算。这时候需要用到Number类中的一些方法。如果没有进行约束,泛型类型是不能调用Number类中的方法的。下面是对类型形参进行约束的代码示例:

错误的写法:

class NumericFns
{ T num; NumericFns(T t) { num = t; } double getDoubleValue() { //这是错误的写法,因为编译后num被当做Object类型,Object类型没有doubleValue()方法。 return num.doubleValue(); }}

正确的写法:

class NumericFns
{ T num; NumericFns(T t) { num = t; } double getDoubleValue() { //这是正确的写法,因为编译后num被当做Number类型,Number类型有doubleValue()方法。 return num.doubleValue(); }}

上面这个例子类型形参T被限定为Number类的子类。

另外,一个形参可以用来约束另一个形参:

class Pair
{ T first; V second; Pair(T a,V b) { first = a; second = b; }}

上面这个例子中,T可以是任何类型,但是一旦指定了T,V就必须是T的子类型。下面这段使用代码就是错的,因为String不是Number的子类:

Pair
z = new Pair
(10,"10");

3. 使用通配符实参以及约束通配符

如果要写一个算法,要对一个浮点数的绝对值和一个双精度数的绝对值进行比较,该怎么办呢?看下面的代码示例:

class NumericFns
{ T num; NumericFns(T t) { num = t; } boolean absEqual(NumericFns
ob) { if(Math.abs(num.doubleValue())==Math.abs(ob.num.doubleValue())) { return true; } return false; }}

根据上面的代码,我们可以写出使用代码:

NumericFns
f = new NumericFns
(1.25f);NumericFns
d = new NumericFns
(-1.25);f.absEqual(d);

注意,代码中?代表的是实参,表示该方法可以传入任何Number类型的实参。这样就达到了让不同类型数据进行比较的目的。通配符实参在不确定实参类型的场景下非常实用。

另外,也可以对通配符进行约束:

//上层约束
//下层约束

4. 使用泛型需要注意的地方

4.1 泛型的作用范围

① 泛型的形参可以在类、接口、构造函数、方法(包括成员方法和静态方法)中声明。

② 类和接口中声明的形参可以使用在:成员变量、成员方法参数、成员方法返回类型。不能用在静态变量和静态方法上(虽然静态方法可以定义自己的形参,但是不能用类和接口中声明的形参,因为类和接口中声明的形参是给对象用的,而不是给类用的)。

class NumericFns
{ //这是对的 T num; //这是错的 static T num1; NumericFns(T t) { num = t; } //这是对的 T getNum() { return num; } //这是错的 static T getNum1() { return num; } //这是对的 static
V getNum2(V v) { return v; }}

③ 泛型的形参不可以是基本数据类型。

4.2 类型形参不能实例化

下面这段代码是错误的,因为编译器不知道要创建哪一种对象,T只是一个占位符:

class Gen
{ T ob; T vals[]; Gen() { //这是错的 ob = new T(); //这也是错的 vals = new T[10]; }}

对于数据,还有一个限制:

//这是错误的Gen
gens[] = new Gen
[10];//这是对的Gen gens[] = new Gen[10];

4.3 歧义错误

下面这两个set方法就会产生冲突,如果T和V都传入String实参,就会产生歧义,这是编译不能通过的。

class MyGenClass
{ T ob1; V ob2; void set(T o) { ob1 = o; } void set(V o) { ob2 = o; }}

4.4 实现了泛型接口的类,其自身也必须是泛型的

4.5 方法中声明的形参,一定要用在方法参数中

4.6 对于形参的声明都放在<>里面

4.7 扩展了Throwable的类都不能使用泛型,也就是说不支持泛型异常类

5. 反射和泛型

从Java 5以后,Class类是泛型的。String.class实际上是Class<String>的一个实例(唯一的实例)。

public static 
Pair
makePair(Class
c) throws InstantiationException,IllegalAccessException { return new Pair<>(c.newInstance(),c.newInstance());}

makePair方法如果传入一个String.class,就会返回一个Pair<String>实例。这在构建框架代码时,非常常见。

反射是一个非常大的动态编程主题,后面会专门讨论它。

 

转载于:https://my.oschina.net/leaforbook/blog/1842251

你可能感兴趣的文章
windows上pip install 报编码错误
查看>>
boost asio学习笔记 [1] - 同步通讯
查看>>
什么是BMC商业模式?
查看>>
不同浏览器中单选框和文字对齐的兼容
查看>>
Python 浮点数在列表中排序的问题
查看>>
一个失业三年后,又重新找回自信的小伙靠的是什么?
查看>>
JFinal学习-Excel导出
查看>>
linuxbridge 小贴士
查看>>
红旗inWise操作系统V8.0发布了!!!
查看>>
tiles2
查看>>
vi 合并多个文件
查看>>
切换npm源
查看>>
细数JDK里的设计模式
查看>>
Oracle中增加Split函数
查看>>
nagios 报警频率控制
查看>>
scrapy 应用
查看>>
Redis 部署策略
查看>>
2011-04-18 python 文件copy 之道 大全 (转)
查看>>
Mybatis Generator的model生成中文注释,支持oracle和mysql(通过实现CommentGenerator接口的方法来实现)...
查看>>
crate安装使用
查看>>