洋艹匆 發表於 2021-10-15 10:10:00

Go:我有注解,Java:不,你没有!

<p data-tool="mdnice编辑器">https://mp.weixin.qq.com/s/hrsagmDtjt6r9fJKf8SUcQ</p>
<p data-tool="mdnice编辑器">特殊在于,Go 有些特性是其他语言有,他没有的。最经典的就是 N 位 Java 同学寻找 Go 语言的注解在哪里,总要解释。</p>
<p data-tool="mdnice编辑器">为此,今天煎鱼就带大家了解一下 Go 语言的注解的使用和情况。</p>
<h2 data-tool="mdnice编辑器">什么是注解</h2>
<h3 data-tool="mdnice编辑器">了解历史</h3>
<p data-tool="mdnice编辑器">注解(Annotation)最早出现自何处,翻了一圈并没有找到。但可以明确,在注解的使用中,Java 注解最为经典,为了便于理解,因此我们基于 Java 做初步的注解理解。</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/KVl0giak5ib4iaHlscIWrCXbhIeHv2nWyJG4EwPwPxu4VGohPMwqspr5iaKNicexGRLzs3joVBVia537GE6oKEQWUcibA/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" data-type="png" data-w="2482" data-fail="0"></p>
<p data-tool="mdnice编辑器">在 2002 年,JSR-175 提出了 《A Metadata Facility for the Java Programming Language》,也就是为 Java 编程语言提供元数据工具。</p>
<p data-tool="mdnice编辑器">这就是现在使用最广泛地注解(Annotation)的来源。示例如下:</p>
<pre data-tool="mdnice编辑器"><code>//&nbsp;@annotation1<br>//&nbsp;@annotation2<br>func&nbsp;Hello()&nbsp;string&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;""<br>}<br></code></pre>
<p data-tool="mdnice编辑器">在格式上均以 “@” 作为注解标识来使用。</p>
<h3 data-tool="mdnice编辑器">注解例子</h3>
<p data-tool="mdnice编辑器">摘抄自 @wikipedia 的一个注解例子:</p>
<pre data-tool="mdnice编辑器"><code>&nbsp;&nbsp;//等同于&nbsp;@Edible(value&nbsp;=&nbsp;true)<br>&nbsp;&nbsp;@Edible(true)<br>&nbsp;&nbsp;Item&nbsp;item&nbsp;=&nbsp;new&nbsp;Carrot();<br><br>&nbsp;&nbsp;public&nbsp;@interface&nbsp;Edible&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;boolean&nbsp;value()&nbsp;default&nbsp;false;<br>&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;@Author(first&nbsp;=&nbsp;"Oompah",&nbsp;last&nbsp;=&nbsp;"Loompah")<br>&nbsp;&nbsp;Book&nbsp;book&nbsp;=&nbsp;new&nbsp;Book();<br><br>&nbsp;&nbsp;public&nbsp;@interface&nbsp;Author&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;first();<br>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;last();<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;//&nbsp;该标注可以在运行时通过反射访问。<br>&nbsp;&nbsp;@Retention(RetentionPolicy.RUNTIME)&nbsp;<br>&nbsp;&nbsp;//&nbsp;该标注只用于类内方法。<br>&nbsp;&nbsp;@Target({ElementType.METHOD})<br>&nbsp;&nbsp;public&nbsp;@interface&nbsp;Tweezable&nbsp;{<br>&nbsp;&nbsp;}<br><br></code></pre>
<p data-tool="mdnice编辑器">在上述例子中,通过注解去做了一系列的定义、声明、赋值等。若是对语言既有注解不熟,或是做的比较复杂的注解,就会有一定的理解成本。</p>
<p data-tool="mdnice编辑器">在业内也常常会说,<strong>注解就是 “在源码上进行编码”</strong>,注解的存在,有着明确的优缺点。你觉得呢?</p>
<h2 data-tool="mdnice编辑器">注解的作用</h2>
<p data-tool="mdnice编辑器">在注解的的作用上,分为如下几点:</p>
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
<li>为编译器提供信息:注释可以被编译器用来检测错误或支持警告。</li>
<li>编译时和部署时处理:软件工具可以处理注释信息以生成代码、XML文件等。</li>
<li>运行时处理:有些注解可以在运行时检查,并用于其他用途。</li>
</ol>
<h2 data-tool="mdnice编辑器">Go 注解在哪里</h2>
<h3 data-tool="mdnice编辑器">现状</h3>
<p data-tool="mdnice编辑器">Go 语言本身并没有原生支持强大的注解,仅限于以下两种:</p>
<ul class="list-paddingleft-2" data-tool="mdnice编辑器">
<li>编译时生成:go:generate</li>
<li>编译时约束:go:build</li>
</ul>
<p data-tool="mdnice编辑器">但这先按不足以作为一个函数注解来使用,也无法形成像 Python 那样的装饰器行为。</p>
<h3 data-tool="mdnice编辑器">为什么不支持</h3>
<p data-tool="mdnice编辑器">Go issues 上有人提过类似的提案:</p>
<p>&nbsp;</p>
<p data-tool="mdnice编辑器">Go Contributor @ianlancetaylor 给出了明确的答复,<strong>Go 在设计上更倾向于明确的、显式的编程风格</strong>。</p>
<p data-tool="mdnice编辑器">思考的优缺点如下:</p>
<ul class="list-paddingleft-2" data-tool="mdnice编辑器">
<li>优势:不知道 Go 能从添加装饰器中得到什么好处,没能在 issues 上明确论证。</li>
<li>缺点:是明确的,会存在意外设置的情况。</li>
</ul>
<p data-tool="mdnice编辑器">因如下原因,没有接受注解:</p>
<ul class="list-paddingleft-2" data-tool="mdnice编辑器">
<li>对比现有代码方法,这种装饰器的新的方法没有提供比现有方法更多的优势,大到足矣推翻原有的设计思路。</li>
<li>社区内的投票,支持的也很少(基于表情符号的投票),用户反馈不多。</li>
</ul>
<p data-tool="mdnice编辑器">可能有小伙伴会说了,有注解做装饰器了,代码会简洁不少。</p>
<p data-tool="mdnice编辑器">对此 Go 团队的态度很明确</p>
<p data-tool="mdnice编辑器">Go 认为<strong>可读性更重要</strong>,如果只是额外多写一点代码,在权衡后,还是可以接受的。</p>
<h2 data-tool="mdnice编辑器">用 Go 实现注解</h2>
<p data-tool="mdnice编辑器">虽然 Go 语言官方没有原生的完整支持,但开源社区中也有小伙伴已经放出了大招,借助各项周边工具和库来实现特定的函数注解功能。</p>
<p data-tool="mdnice编辑器">GitHub 项目分别如下:</p>
<ul class="list-paddingleft-2" data-tool="mdnice编辑器">
<li>MarcGrol/golangAnnotations</li>
<li>u2takey/go-annotation</li>
</ul>
<p data-tool="mdnice编辑器">使用示例如下:</p>
<pre data-tool="mdnice编辑器"><code>package&nbsp;tourdefrance<br><br>//go:generate&nbsp;golangAnnotations&nbsp;-input-dir&nbsp;.<br><br>//&nbsp;@RestService(&nbsp;path&nbsp;=&nbsp;"/api/tour"&nbsp;)<br>type&nbsp;TourService&nbsp;struct{}<br><br>type&nbsp;EtappeResult&nbsp;struct{&nbsp;...&nbsp;}<br><br>//&nbsp;@RestOperation(&nbsp;method&nbsp;=&nbsp;"PUT",&nbsp;path&nbsp;=&nbsp;"/{year}/etappe/{etappeUid}"&nbsp;)<br>func&nbsp;(ts&nbsp;*TourService)&nbsp;addEtappeResults(c&nbsp;context.Context,&nbsp;year&nbsp;int,&nbsp;etappeUid&nbsp;string,&nbsp;results&nbsp;EtappeResult)&nbsp;error&nbsp;{<br>&nbsp;return&nbsp;nil<br>}<br></code></pre>
<p data-tool="mdnice编辑器">对 Go 注解的使用感兴趣的小伙伴可以自行查阅使用手册。</p>
<p data-tool="mdnice编辑器">我们更多的关心,Go 原生都没支持,那么开源库都是如何实现的呢?在此我们借助 MarcGrol/golangAnnotations 项目所提供的思路来讲解。</p>
<p data-tool="mdnice编辑器">分为三个步骤:</p>
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
<li>解析代码。</li>
<li>模板处理。</li>
<li>生成代码。</li>
</ol>
<h3 data-tool="mdnice编辑器">解析 AST</h3>
<p data-tool="mdnice编辑器">首先,我们需要用用 go/ast 标准库获取代码所生成的 AST Tree 中需要的内容和结构。</p>
<p data-tool="mdnice编辑器">示例代码如下:</p>
<pre data-tool="mdnice编辑器"><code>parsedSources&nbsp;:=&nbsp;ParsedSources{<br>&nbsp;&nbsp;&nbsp;&nbsp;PackageName:&nbsp;"tourdefrance",<br>&nbsp;&nbsp;&nbsp;&nbsp;Structs:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[]model.Struct{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DocLines:&nbsp;&nbsp;&nbsp;[]string{"//&nbsp;@RestService(&nbsp;path&nbsp;=&nbsp;"/api/tour"&nbsp;)"},<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Name:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"TourService",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Operations:&nbsp;[]model.Operation{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DocLines:&nbsp;&nbsp;&nbsp;[]string{"//&nbsp;@RestOperation(&nbsp;method&nbsp;=&nbsp;"PUT",&nbsp;path&nbsp;=&nbsp;"/{year}/etappe/{etappeUid}"},<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;},<br>}<br></code></pre>
<p data-tool="mdnice编辑器">我们可以看到,在 AST Tree 中能够获取到在示例代码中所定义的注解内容,我们就可以依据此去做很多奇奇怪怪的事情了。</p>
<h3 data-tool="mdnice编辑器">模板生成</h3>
<p data-tool="mdnice编辑器">紧接着,在知道了注解的输入是什么后,我们只需要根据实际情况,编写对应的模板生成器 code-generator 就可以了。</p>
<p data-tool="mdnice编辑器">我们会基于 text/template 标准库来实现,比较经典的像是 kubernetes/code-generator 是一个可以参考的实现。</p>
<p data-tool="mdnice编辑器">代码实现完毕后,将其编译成 go plugin,便于我们在下一步调用就可以了。</p>
<h3 data-tool="mdnice编辑器">代码生成</h3>
<p data-tool="mdnice编辑器">最后,万事俱备只欠东风。差的就是告诉工具,哪些 Go 文件中包含注解,需要我们去生成的。</p>
<p data-tool="mdnice编辑器">这时候我们可以使用&nbsp;<code>//go:generate</code>&nbsp;在 Go 文件声明。就像前面的项目中所说的:</p>
<pre data-tool="mdnice编辑器"><code>//go:generate&nbsp;golangAnnotations&nbsp;-input-dir&nbsp;.<br></code></pre>
<p data-tool="mdnice编辑器">声明该 Go 文件需要生成,并调用前面编写好的 golangAnnotations 二进制文件,就可以实现基本的 Go 注解生成了。</p>
<h2 data-tool="mdnice编辑器">总结</h2>
<p data-tool="mdnice编辑器">今天在这篇文章中,我们介绍了注解(Annotation)的历史背景。同时我们针对 Go 语言目前原生的注解支持情况进行了说明。</p>
<p data-tool="mdnice编辑器">也面向为什么 Go 没有像 Java 那样支持强大的注解进行了基于 Go 官方团队的原因解释。如果希望在 Go 实现注解的,也提供了相应的开源技术方案。</p>
<p data-tool="mdnice编辑器"><strong>你觉得 Go 语言是否需要像和 Java 一样的注解支持</strong>呢?</p>
<p data-tool="mdnice编辑器">欢迎在评论区交流和讨论!</p><br><br>
来源:https://www.cnblogs.com/cheyunhua/p/15409847.html
頁: [1]
查看完整版本: Go:我有注解,Java:不,你没有!