Minecraft(我的世界)中文论坛

标题: [教程系列]利用Annotation与反射实现更加整洁易用的子命令

作者: RE_OVO    时间: 2016-12-9 17:51
标题: [教程系列]利用Annotation与反射实现更加整洁易用的子命令
本帖最后由 RecursiveG 于 2016-12-18 06:40 编辑

论坛竟然没有滑稽表情,差评!
大家平时写子命令都是都是用的switch或者if吧,但是这种方法在子命令多的情况下特别蛋疼,代码很乱,各种嵌套,影响阅读。
今天我讲给各位介绍利用Annotation+反射实现更加方便清晰易用的子命令。
什么是Annotation?   百度去
什么是反射?    百度去

接下来,我将以一个/xxx命令为梨子,来介绍利用Annotation写子命令

首先,你需要准备的是:
  电脑
  大脑
  双手
  滑稽

打开我们的IDE,我创建一个工程,并且完成包,类的,创建。并且在plugin.yml里注册xxx命令。
他看起来是这样的额


接下来,我们在onEnable方法里注册命令,并创建命令类。



一切看起来似乎很正常!
接下来才是正文!
我们创建一个Annotation类,就叫他SubCommand



@interface是定义Annotation类的关键字

@Target(ElementType.METHOD) 指的是用于方法的声明
@Retention(RetentionPolicy.RUNTIME) 指的是运行时加载Annotation到JVM中
default 表示这个成员的默认值。
不懂没关系,记住就行。
然后我们在里面定义了3个成员,后面会用到。
回到我们的命令方法,我们添加一个无参数的判断,输出所有子命令。
如图:


这里我们遍历了本类的方法,判断是否有SubCommand注释,如果有,获取这个方法的注释并拼接帮助并发送消息。
很多人问为什么要遍历本类方法?因为我们把子命令写在了这个类,下面看下去你就知道了。
然后,有帮助还不行,还有执行子命令啊,于是,如图:


我们同样遍历本类方法,判断SubCommand注释,并且判断注释中的cmd是否和输入的参数相等,相等就使用反射执行此方法,并return true退出方法。
然后我们再加个找不到子命令的提示


最后,完整的onCommand()方法是这样的:

好了,命令轮子造完了,现在我们几乎不需要再去动他了,我们只需要在下面添加命令就行了。@SubCommand(cmd="子命令内容",arg="参数介绍",des="命令介绍")
public void 方法名字随便写(Player p,String args[]){
//执行此子命令
}
为什么参数是Player p和String args[]呢?
因为,我们上面利用反射执行的时候传进来就是这两个参数,而我们也需要这2个参数啊,不然子命令怎么知道执行者和其他的参数?
方法名随意,因为靠的是上面的@SubCommand注释来判断的
然后我们添加2个字命令进行测试:

由于我们的命令帮助是靠遍历Annotation自动生成的,我们也不需要去改动onCommand方法
我们来测试下:


很棒是不是
而且代码清晰可读性高,添加子命令快速,自动生成帮助。

好了,教程就到这里
求人气求回复

禁止转载!禁止转载!
作者: wansi    时间: 2016-12-10 11:33
前排,支持!
作者: 耗子    时间: 2016-12-10 18:27
反射使效率降低了不少
作者: RE_OVO    时间: 2016-12-10 19:12
耗子 发表于 2016-12-10 18:27
反射使效率降低了不少

这只是命令而已,效率问题不大
我觉得还是美观易用最重要
switch/if写出来各种嵌套看的辣眼睛。
作者: 耗子    时间: 2016-12-10 19:45
jebme 发表于 2016-12-10 19:12
这只是命令而已,效率问题不大
我觉得还是美观易用最重要
switch/if写出来各种嵌套看的辣眼睛。 ...

与其写个教程,不如写个API
作者: a8105    时间: 2016-12-12 02:15
耗子 发表于 2016-12-10 19:45
与其写个教程,不如写个API

这就涉及到一个名言了
授人以鱼不如授人以渔←_←
当然,像什么用到nms之类的,代码多的,还是鱼好了

作者: 桃渊林    时间: 2016-12-12 23:45
有人写过类似的但并没有分享。。
可以考虑写成API前置嘛
作者: 12345559    时间: 2016-12-15 09:07
我觉得还是发教程好,自己写比调用的好多了
作者: CCU    时间: 2016-12-15 17:37
只是一个用不用的问题,反射更适合用在其他类里
作者: Beam_less    时间: 2016-12-15 20:03
虽说反射降低了效率 但是可以把反射后的结果 比如Method 先存起来 下次就直接用了
Bukkit的事件系统 在registerEvents之后 似乎也是这样做的 对效率的影响没那么大了 起码不会再遍历一遍了
作者: 叶米柯    时间: 2016-12-15 20:25
反射卡卡卡……………………(省略1000个卡)…………卡,好的,反射比直接调用卡1000倍左右,这也是为什么Mod服带不了特别多的人,如果你要forClass的话还要更卡
当然如果你只是调用指令的话没问题,但是不建议在Java中过度使用反射,你要想啃语言糖就去用scala
作者: 土球球    时间: 2016-12-15 23:00
如果不考虑Java6的支持,为何不直接使用MethodHandle呢= =
作者: awt2003    时间: 2017-1-25 14:51
我有应该问题,子命令下的子命令该如何解决?
比如/am automsg enable
作者: RE_OVO    时间: 2017-1-25 15:27
awt2003 发表于 2017-1-25 14:51
我有应该问题,子命令下的子命令该如何解决?
比如/am automsg enable

这个直接在子命令执行里判断参数吧
作者: azbh111    时间: 2017-6-22 13:33
叶米柯 发表于 2016-12-15 20:25
反射卡卡卡……………………(省略1000个卡)…………卡,好的,反射比直接调用卡1000倍左右,这也是为什么Mo ...

MOD服带不了特别多人与反射有啥关系?

难道是MOD服大量使用反射?  MOD在使用还是FORGE在使用?
作者: mai1015    时间: 2017-6-28 08:21
12345559 发表于 2016-12-15 09:07
我觉得还是发教程好,自己写比调用的好多了

此人骗走我 600块,不信的可以对质。
此人人品及道德价值观严重有问题!!请大家注意!!

请问你还有脸混在MCBBS??还有脸混于插件界?
你的做法以及人品道德价值观,你家人,你同学,你朋友知道吗?
作者: mai1015    时间: 2017-6-28 17:16
mai1015 发表于 2017-6-28 08:21
此人骗走我 600块,不信的可以对质。
此人人品及道德价值观严重有问题!!请大家注意!!

骗钱还有前因?因为他穷?
作者: liuyipeng001    时间: 2017-8-5 17:26
写成一个类的看戏
作者: switefaster    时间: 2018-1-9 16:20
azbh111 发表于 2017-6-22 13:33
MOD服带不了特别多人与反射有啥关系?

难道是MOD服大量使用反射?  MOD在使用还是FORGE在使用? ...

主要是因为Mod的各种功能例如识别主类,识别服务端/客户端,代理等都是用Annotation实现的。
当然卡的要死
作者: azbh111    时间: 2018-1-9 22:34
switefaster 发表于 2018-1-9 16:20
主要是因为Mod的各种功能例如识别主类,识别服务端/客户端,代理等都是用Annotation实现的。
当然卡的要死 ...

你确定不是因为单线程....
作者: 小沈同学    时间: 2018-1-12 08:31
好用,已收藏
作者: Kirito刀剑神域    时间: 2018-2-15 17:07
挖个坟。MethodHandle在效率上更好。其实替换if/elseif,java也有一些成熟的模式。不过个人喜欢造轮子,搞一套解析。这样有OO的感觉2333
作者: RE_OVO    时间: 2018-2-18 16:15
Kirito刀剑神域 发表于 2018-2-15 17:07
挖个坟。MethodHandle在效率上更好。其实替换if/elseif,java也有一些成熟的模式。不过个人喜欢造轮子,搞 ...

目前我喜欢用抽象类
写个SubCommand抽象类
然后子命令继承
把这些SubCommand丢到主命令里的List

作者: Kirito刀剑神域    时间: 2018-2-20 11:11
jebme 发表于 2018-2-18 16:15
目前我喜欢用抽象类
写个SubCommand抽象类
然后子命令继承

对,我就是采用这种方式。
不过增加了简单的解析。比如固定一个模式,就直接提取参数。
作者: 936796603    时间: 2018-8-17 01:53
图崩了
作者: ksqeib445    时间: 2018-8-28 01:32
图全挂啦,兄弟!
作者: 酷乐工作室    时间: 2018-8-28 13:39
图挂完了,lz补一下,看着不舒服
作者: MayDayMemory    时间: 2020-2-24 15:25
图挂了。。。
作者: MayDayMemory    时间: 2020-2-24 15:51
Kirito刀剑神域 发表于 2018-2-15 17:07
挖个坟。MethodHandle在效率上更好。其实替换if/elseif,java也有一些成熟的模式。不过个人喜欢造轮子,搞 ...

有没有感觉用map其实也可以..如果把子命令也按照命令模式来写。先弄个Order接口,里头放一个execute(CommandSender sender,String label,String args[])。写子命令的时候就接口Order。如果用CommandMap注册父命令的话,不是要传个实例进去嘛。可以在父命令的构造方法里实现子命令的“注册”,也就是把子命令的名称和子命令的实例放进Map<String,Order>里。父命令execute的时候就直接检索子命令然后execute。建包的时候按照父命令的名字来建,比如说/abc的子命令全都放到abcSubcommand这个包里,这样文件视图不也清晰起来了。
作者: Handsome男孩    时间: 2020-2-25 13:54
这个软件真的太棒了,正好需要
作者: MZIMU    时间: 2020-2-25 14:39
XD。。图炸拉