提问: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的一种设计模式。

你觉得呢?欢迎评论区留言讨论!

posted @ 2022-07-28 22:55  久曲健  阅读(545)  评论(0编辑  收藏  举报