Java集合中使用泛型参数及泛型上下限的问题

来源:互联网 发布:转视频格式的软件 编辑:程序博客网 时间:2024/05/24 22:44

今天在写一个多线程调度器,先花了十分钟把逻辑理清,需求如下:

1.我们有一张数据库表(单表),存放着不同来源的订单(所有来源的订单在存入这张表之前都会转化为这张表规定的字段格式),假设一共十个不同的渠道来源。

2.然后我们需要定时轮询这张表,对所有订单向对应渠道发起校验,以及监测订单是否是否过期。

3.轮询的规则是这样的,根据指定渠道查询这张表中该渠道的所有订单,然后进行相应处理


我的解决思路是:

首先,给每个渠道定义一个对应的任务,这个任务要完成的就是轮询数据库中所有该渠道的订单并进行处理,使用Map作为渠道的任务池。有了任务,还得让它跑起来呀,而且还不能让它无限制的运行,所以考虑用Future<Boolean>来给每一个任务定时,所以把前面的梳理一下,我们要把每个渠道的任务,以及每个任务的一次执行对应起来(读者可以先想想你们会用到什么数据结构来把这三者存储起来)。

但我们总共有十个任务呢,如果让它们各自运行那岂不是像一盘散沙吗,所以我们还需要一个任务调度器,这个任务调度器负责把不同的渠道对应的任务添加进任务池,然后轮询任务池,并执行任务池中的任务,同时还要把每个任务的每次执行存储起来,方便我们对它们进行监测,防止有任务无限执行下去。

最后,我们的十个任务也不需要时刻运行,每十分钟触发一次。所以我们还需要一个任务触发器,每十分钟启动一次任务。


好了,思路终于理清了,再来进行具体的设计

1.先设计数据结构,用一个嵌套的Map来把渠道---任务---执行结果对应起来

Map<String, Map<? extends Callable<Boolean>,Future<Boolean>>>

2.给每一个渠道定义一个Task,实现Callable<Boolean>接口,完成向对应渠道发起校验以及过期检测的任务;

3.写一个TaskDispatcher类(正确的设计方式其实是定义一个接口,里面添加流程中涉及到的方法声明,然后让这个类实现这个接口),在里面实现添加如下几个方法:

addChannel(String channel){}//这个方法用来向任务池中添加渠道对应的任务

executeTask(String channel){}//执行对应渠道的任务,并获取任务执行的Future<Boolean>存储起来

pollTasks(){} //轮询任务池中的所有任务,并执行它们

4.写一个任务触发器,每十分钟启动一次所有任务。

ps:写到这里我突然想到,如果我们不需要关心任务执行的结果,只需要知道它有没有在规定的时间执行完,我们完全可以把把任务结果独立拉出来放在一个List中,然后另起一个任务轮询List中的所有任务结果,如果发现没有再规定时间内完成的则结束它们。这样我们就免去了把渠道---任务---执行结果对应起来的大麻烦。


接下来撸代码的时候遇到这样一个问题,我首先定义了任务池

private Map<String, Map<? extends Callable<Boolean>,Future>> map= new WeakHashMap<String,Map<? extends Callable<Boolean>,Future<Boolean>>>();

然后添加任务的时候出错了

Map<? extends Callable<Boolean>,Future> tfMap=new HashMap<? extends Callable<Boolean>,Future>();   

tfMap.put(new Task(), null); 

错误提示如下:

The method add(capture#3-of ? extends A) in the type List<capture#3-of ? extends A> is not applicable for the arguments (A)

我百思不得其姐大哭,问题究竟出在哪里呢?

我又试着把new HashMap<? extends Callable<Boolean>,Future>();  换成new HashMap<>(); 

结果还是报错,于是我开始翻阅各种资料博客,试尽了各种办法,始终报错。就在我准备放弃的时候无意间在一篇博客中瞥见这样一句话:

集合中使用泛型限定:? extends Super(或? super Child),这种集合是只读的,因为“表达式右边永远是一个确定的值”

上面这句话很耐人寻味,我细细推敲了一下恍然大悟

从源头上来思考,泛型的作用就是限定某一种容器中只能装某一个特定的类对象,而泛型通配只是限定了范围,实际内存中还是要求某一容器必须装同种类对象,这就是所谓的“表达式右边永远是一个确定的值”,因为表达式右边代表内存中的一块内容,左边是给方便给程序员看的,是对这块内存中的内容起的一个别名,换句话说,程序在运行的时候必须能确定我要分配多大的内存。

具体到这个例子上来分析就是,map只是我们抽象出的一个容器,提醒我们这个容器里面可以放哪些类型(或范围内)的东西,由于Java的多态,我们能从里面取出我们想要的对象(因为可以用父类表示子类对象),这就是所谓的“只读性”;可是如果要往里存对象的时候,我们就需要明确告诉JVM我要存哪一种具体的类对象,这样JVM才可以分配出指定大小的内存来存储对象。

理解了上面的内容,我们就可以作出如下修改:

Map<Task,Future> tfMap=new HashMap<Task,Future>();   //分配一个装载特定类对象的容器

tfMap.put(new Task(), null);    //现在添加就不会出错


最后我又尝试了写一个最简单的例子来验证我的理解,事实证明的确如此:

class A{}
class B extends A{}

List<? extends A> list=new ArrayList<A>();
list.add(new A());  //报错
list.add(new B());  //报错
list=Arrays.asList(new A(),new B());  //正确
List<? extends A> list2=new ArrayList<A>();
list2.add(new A()); //报错
list2.add(new B()); //报错
list=Arrays.asList(new A(),new B()); //正确

Arrays.asList之所以能能成功是因为,内部有一个new ArrayList<A>()操作,申请了一个装载特定类对象的容器
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 鱼丸捕鱼大作战换手机了怎么办 2o岁j'j小怎么办 为什么小米5s指纹不能用怎么办 cs录屏软件运行内存太大了怎么办 可是没有他我怎么办啊是什么电视剧 可是没有他我怎么办啊是哪个电视剧 手机太卡了打字打不了了怎么办 梦幻西游的将军令没有电了怎么办 将军令全部的序列号都忘记了怎么办 船员证被公司压着想自己换证怎么办 电子录入系统中无法打开影像怎么办 火车票退票后说银行退款失败怎么办 苹果4s玩游戏闪退怎么办 买了二手房原房主不迁户口怎么办 苹果禁反忘记工id密码了怎么办 玩英雄联盟用腾讯游戏平台卡怎么办 游戏代练接单了没有给我账号怎么办 华为手机进入设置立即闪退怎么办 股东发现公司有做假账现象怎么办 中国在服役期间有纹身被发现怎么办 脚碰了肿了紫了怎么办 外阴出血了怎么办去医院检查没问题 三个半月宝宝体检脚有的紧怎么办 肛门被红枣核刺了一个洞怎么办 肛门里面有棉签上的棉花怎么办 孩子裤子沾屎怎么洗下来怎么办 做完痔疮手术后有点肛门狭窄怎么办 孕妇做b超宝宝不配合怎么办 怀孕产检医生问的尴尬怎么办 带着节育环做的核磁怎么办 便秘洗肠后最一周未排便怎么办 用了开塞露后肚子疼拉不出来怎么办 冰点脱毛当天用沐浴露洗澡了怎么办 自体脂肪填充脸部但发红又痒怎么办 金矿受伤死亡不给开死亡证明怎么办 手机欠费了导致没信号了怎么办 金立手机指纹硬件无法使用怎么办 试管取卵医生说卵子碎片多怎么办 取卵腹水抽水后尿不通怎么办 手机锁屏密码忘了怎么办求解锁 苹果手表锁屏密码忘记了怎么办