数据结构(泛型和包装类)

泛型

我现在手上有一些不同类型的数据,有整型的、浮点型的、还有字符串。现在来想,我想将这些数据放到数组中,就需要不同类型的数组。要是有一个数组,可以存放不同数据类型的数据,并且在存放的时候记录数据的类型就好了。
我似乎有了一些想法:
一个神奇的类

此时我定义了一个类,类里首先定义了一个Object类型的数组,因为Object类是所有类父类,所以我定义Object类的数组,它理论上是可以接收任何形式的数据。
接下来我定义了一个setVal方法,输入下标pos和值val,将数据传入指定的位置。
最后我定义了getPos方法,输入指定的下标pos,就能得到对应的值。

这似乎是一个完美的解决方案,因为我的确可以将所有类型的值传入这个类中。但是事实好像不是这样。 当我要取数据的时候,发生了这样的事:
数据类型不统一
此时我发现,这个类的数据在传出的时候,统一都是Object类!也就是说,我必须要进行强转才能接收数据。如果我有一百个或者一万个数据要传出,难道我每一个都要进行类型的强转吗?不,这件事我不能接受。此时我的舍友小王告诉我,你可以使用Java中的语法——泛型。

什么是泛型

一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。----- 来源《Java编程思想》对泛型的介绍。
泛型最主要的目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。
泛型类
现在,MyArray变成了一个泛型类,是泛型标志,标志这个类是一个泛型类。同时可以看到,原来类里所有的Object全部被T所替换。泛型类的使用方法如下:
泛型类的使用
由上图中可以看到,泛型类在定义的时候,需要在<>里写入将要给这个类传入的数据类型。例如图中就是给这个类传入int类型的数据。在传入数据的时候,除了int类型的数据都无法传入,图中的“apple”和4.534都会报错,另外,再取数值的时候,也不需要对数据进行强转。

泛型是编译时期的一种机制,叫做擦除机制。运行的时候是没有泛型的概念的

【规范】类型形参一般使用一个大写字母表示,常用的名称有:
E 表示 Element
K 表示 Key
V 表示 Value
N 表示 Number
T 表示 Type
S, U, V 等等 - 第二、第三、第四个类型
尖括号里必须是类类型,不能是基本类型

泛型的上界

在定义一个泛型类的时候,需要对传入的类型变量进行约束,可以通过类型边界来进行约束。

语法:class 泛型类名称<类型形参 extends 类型边界> { ... }

泛型上界
此时有一个类Array,它的泛型上界为Number。也就是说,Array的数据类型只能是Number和其子类。图中可以看到,Integer和Long都是Number的子类,因此可以创建类,但是String不是Number的子类,所以不能创建类。

泛型没有下界

泛型方法

泛型类可以做到放入不同类型的数据,但是我在调用类里的方法的时候还需要创建类,太不方便,我可不可以将泛型类里的方法变成静态方法(static)?
尝试给泛型类里的方法加static
怪了,加了static之后,为啥程序报错了?
其实仔细想想,便能知道为什么。前面不是静态的方法在调用时,先创建了对象,再调用的方法,而现在方法变成静态的之后,便可以直接使用类名调用方法了。而泛型类作为有点特殊的类,它在创建对象的时候会传递数据类型,静态方法不创建对象,无法传递数据类型,当然就报错了。
那我们将要传递的数据类型放在方法头上可不可以?当然可以,这就是泛型方法
泛型方法
上图中,func方法头上加了一个< E extends Number> 变成了一个泛型方法,此时的类Array就不是一个泛型类了。可是现在又出现一个问题:这个泛型方法在调用的时候,我也没看见传递了数据类型啊?其实已经传递了,完整的代码应该是这样的:

Integer a = Array.<Integer>func(10);

通配符

现在我有一个Message泛型类,然后我在main方法里定义了String类型的Message对象并传入字符串“hello world”。func方法用于将message方法里的信息打印出来。但是func方法要传入的message对象被指定为了String类型,我现在message的类型变成了整数类型该怎么办,再写一个func2?显然不可能。
没有通配符的情况
我只需要将func里的参数泛型用通配符占位,问题就迎刃而解~
使用通配符

通配符是个好东西,让不同类型的数据都可以传入。等等,我似乎闻到了多态的味道,这个通配符和方法重载有什么关系?我们现在看下面的代码:
通配符与重载
现在我定义了一个泛型类Apple,main方法中new了两个对象apple1(接受Integer)和apple2(接受String)。那么现在来说,这应该是两个类吗?其实不是,从输出结果来看,编译器仍认为这是一个类的两个对象。也就是说不管是方法还是类,加上通配符之后并不能让编译器认为成是两个东西,因为在运行的时候,通配符(泛型)就被“擦除”了。
通配符不是重载
在上图中我们看到,我又定义了一个func方法,传入的是String类型Message,但是代码报错。报错原因也写着,这两个func方法有着相同的擦除机制。
事实上,在编译器看来,这两个方法都是长这样子:
public static void func(Message message){ }

通配符的上下界

通配符上界

<? extends English>

表示通配符的上界
自定义的类继承关系
通配符上界
通配符上界
由上图所示,类American_English继承English类,English类和Chinese类继承Words类。此时新new的message1和message2都是可以放入func方法中的。
在func方法中,因为?定义了上界,因此由于传入的对象编译器不知道是哪个类型,因此无法接收,而传出的类一定是English类,因此可以传出。(不太懂为什么不可以放东西)

通配符下界

<? super English>

表示通配符的下界
通配符下界
此时传入的类必须是English类和其父类。类似的,func方法中可以传入类,因为传入的必是English的父类,只会发生向上转型,因此可以传入。不能取出类,因为不知道取出的类是什么类型,会发生向下转型(好像是和向上和向下转型有关,我也不太清楚)

包装类

包装类型

装箱和拆箱

装箱和拆箱操作

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值