提問: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的一種設計模式。

你覺得呢?歡迎評論區留言討論!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章