提问:Java中的注解与Python中的装饰器是一回事吗?
写在前面
早上刚到公司,被群里这样一个问题所吸引,如下图所示:
后来自己就又想了想,感觉自己说的也不全对,于是想从程序角度来做一个佐证。
当然,在写这篇文章之前,也是查阅了很多文章,关于这个问题的一些观点,汇总如下:
- Java 注解也叫元数据,一种代码级别的说明。Python 装饰器是一种语法糖。
- 注解是给别人看的,功能不仅仅由注解决定;装饰器直接拦截,直接改变被装饰对象的行为!
- 注解(Annotation):仅提供附加元数据支持,并不能实现任何操作。需要另外的 Scanner 根据元数据执行相应操作。
- 装饰器(Decorator):仅提供定义劫持,能够对类及其方法的定义并没有提供任何附加元数据的功能。
讲真这些概念性的东西,我是真的看的云里雾里的,建议还是看维基百科或者教材吧。
我个人观点,肯定是注解和装饰器不是一回事的。
话不多说,还是直接上代码,用实际案例来说话吧!
一、表现形式上看
@zhujie('参数')
1、相同点:
都是@开头,注解、装饰器都可以自定义、都可以带参数、都可以被标注代码块之前执行。
2、不同点:
- java注解可以写在类、方法、变量头上;
- python装饰器可以写在类、方法头上。
二、实例对比
1、Java注解
示例代码如下:
@Override
public String toString() {
return "PlaylistInfo{" +
"curtime='" + curtime + '\'' +
", issmarter='" + issmarter + '\'' +
", xmusicnum='" + xmusicnum + '\'' +
", picurl=" + picurl +
", playlist=" + playlist +
", systemtime=" + systemtime +
'}';
}
@Override: 重写的意思,不满意父类的可以自己在实现下,一般在编译阶段会对方法进行检查。
很明显,注解放在方法上方,仅负责编译、检查,并未对方法中的内容和该方法的功能做出改变。
2、python装饰器
实例代码如下:
class TestClass():
@property
def myValue(self):
return self.value
if __name__=="__main__":
TestClass.myValue = '装饰器呀!'
print (TestClass.myValue)
@property: 作为属性的意思
明显看出,装饰器直接改变了函数的功能。
3、结论
由上得出,注解和装饰器的不同:
- 1、注解对只是干了检查、校验的事,不会修改所标注的代码。
- 2、装饰器可以在方法标注,并改变所修饰的代码功能。
到这里,你是不是会觉得,他俩根本就不是一回事,因为根本不是一样的呀。
其实,在java中的注解和反射可以实现python里装饰器的效果。
是不是又蒙了?别急,我们接着往后看!
二、注解实现上看
注解的好处:在不改动源代码的基础上,对源代码实现新功能。如果有大面积代码需要改动同样功能,可以在方法或者类上面使用注解实现
1、实现的注解场景
分别用python与Java方式,实现对程序计算的校验,把异常结果写到error.log文件中
2、Python方式实现
实例代码如下:
# 此时就是作为写入错误结果使用
def check(func):
def wrapper(*args, **kwargs):
try:
res = func(*args, **kwargs)
return res
except Exception as err:
with open("error.log", mode="at", encoding='utf-8') as f:
f.write("start".center(50, '*'))
f.write('\n')
f.write(str(func))
f.write('\n')
f.write(str(err.__class__))
f.write('\n')
f.write("end".center(50, '*'))
f.write('\n')
return wrapper
@check
def add(a, b):
print(a + b)
@check
def divide(a, b):
print(a / b)
add(50, 50)
divide(100, 0)
3、Java方式实现
示例代码如下:
public class Calculator {
@Check
public void add() {
System.out.println(50 + 50);
}
@Check
public void divide() {
System.out.println(100 / 0);
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
public class CheckDemo {
public static void main(String[] args) throws IOException {
Calculator test = new Calculator();
Class<? extends Calculator> c = test.getClass();
Method[] methods = c.getMethods();
BufferedWriter bw = new BufferedWriter(new FileWriter("input.txt"));
for (Method m :
methods) {
if (m.isAnnotationPresent(Check.class)) {
try {
m.invoke(test);
} catch (Exception e) {
bw.newLine();
bw.write(e.getCause().getMessage()+"----");
bw.newLine();
bw.write(String.valueOf(e.getCause().getClass()));
}
}
}
bw.flush();
bw.close();
}
}
分别运行各自编译器,结果如下图所示:
4、结论
由上可知,Java中的注解和反射可以实现python里装饰器的效果。
三、来个接口开发的例子
1、需求场景
输入用户密码,返回用户信息接口
2、Python方式实现
示例代码如下:
from flask import Flask
import json
from flask import request
app = Flask(__name__) #启动
@app.route('/rongrong/getUserInfo',methods=['GET']) #请求路径、请求方式
def login():
username = request.args.get("username") #获取url中参数“username”的值
password = request.args.get("password") #获取url中参数“password”的值
if username and password: #如果传入了值为真打印下面信息
data = json.dumps({
"username":username,
"password":password,
"code":"200",
"message":"成功"
},ensure_ascii=False) #解决中文乱码问题
else: #如果传参为空打印下面信息
data = json.dumps({
"message":"请传递参数"
},ensure_ascii=False)
return data
if __name__ == "__main__":
app.run() #运行
3、Java方式实现
示例代码如下:
@RequestMapping(value = "/rongrong/getUserInfo", method = RequestMethod.GET)
public Result<List<Student>> findStudentByName(HttpServletRequest request) {
String username = request.getParameter("username");
String password = request.getParameter("password");
JsonRootBean jsonRootBean = new JsonRootBean();
jsonRootBean.setUsername(username);
jsonRootBean.setPassword(password);
jsonRootBean.setCode("200");
jsonRootBean.setMessage("成功");
Result result = ResultUtils.success(jsonRootBean);
return result;
}
4、启动各自服务,运行结果如下:
python结果:
Java结果:
5、结论
Python的装饰器很单一,就是通过一层壳对一个函数的行为进行修饰,而@decorator_func 只是一个语法糖,用以美化装饰器的写法。
Java中的注解则不同,它是从语言层面为代码中的类,函数,字段增加一些运行时可以读到的元数据,而注解的提供者要在运行时对这些元数据进行读取,并做相应的处理。
四、写在最后
笔者才疏学浅,写这篇文正完全是出于技痒,自然也是查阅了大量文章,才有此文。
以下内容仅代表个人观点:
- 长得像,但却是两个物种,不过可以让他们表现得近似;
- Python 的装饰器正如他的名称,很直白,就是实现了装饰器模式(的一个语法糖)。@部分对应一个返回为函数的函数,可以对目标函数进行输入、输出过滤,以及其他干预、包装;
- Java 的注解有好几种,按作用期划分有编译期、运行期等,仅仅是给类或方法做个标记,在对应的时期你的程序可以通过反射读到它们;
- Java 的注解表面看似乎没啥子用,但少就是多,稍微包装一下就可以实现与 Python 装饰器等同的作用,前提是通过什么方式调用目标类和方法,只要调用的包装内对注解进行了解释,就 OK 了;
通过各种手段可以让他们变成一回事儿,所以就结果而言,没错,可以当成是一回事儿。
换句话说,有时候感觉装饰器更像是Java的一种设计模式。
你觉得呢?欢迎评论区留言讨论!
优秀不够,你是否无可替代
软件测试交流QQ群:721256703,期待你的加入!!
欢迎关注我的微信公众号:软件测试君
![](https://www.cnblogs.com/images/cnblogs_com/longronglang/1061549/o_QQ%E6%88%AA%E5%9B%BE20190728134401.jpg)