成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

使用自動代碼生成工具(IDE),Java工程師要注意的那些小瑕疵

譯文
開發 開發工具
我在本文中將介紹幾個錯誤,據我的感受,我們在用Java開發代碼,尤其是使用集成開發環境(IDE)的工具自動生成代碼時經常犯這些錯誤。

【51CTO.com快譯】我在本文中將介紹幾個錯誤,據我的感受,我們在用Java開發代碼,尤其是使用集成開發環境(IDE)的工具自動生成代碼時經常犯這些錯誤。

自動生成代碼很棒,它讓我們少敲打好多代碼,有助于關注領域問題,而不是關注樣本代碼的細枝末節。然而,有時候,我們并不認真思考IDE為我們生成的代碼,我們留下了應該修復的小瑕疵。我在本文中將逐一介紹這些錯誤。

公共域

IDE在默認情況下認為所有類都是公共類。所以,如果我們要求IntelliJ創建一個新的類,就會出現這種情況:

  1. public class MyClass { 

在我看來,類在默認情況下會有包作用域(package scope),即:

 

  1. public class MyClass { 

 

這樣一來,它們在所屬的包之外是不可見的。如果某個時候我們需要更改類的域,就要考慮原因,并且做出便利的設計決定。如果類在默認情況下是公共類,我們就直接拿來使用,從而增加代碼的耦合性。

要避免這個問題,***的辦法就是在我們的IDE中編輯模板。比如在IntelliJ IDEA中,這個選項在偏好設置>編輯器> 文件和代碼模板> 模板>類中。

我們在默認情況下使用包作用域時的***實踐之一就是,它將接口創建為公共接口,但是又不暴露其實現。這樣一來,接口的客戶根本不知道它們使用的實現,因為它們甚至無法訪問它,青睞依賴項注入之類的實踐。

公共域這方面的另一個例子是,IntelliJ在默認情況下將變量創建為公共:

  1. public static final String CONSTANT = "value"

大多數時候,變量只被擁有它們的那個類使用。請注意這個,或者更改你的模板。

不可變類

這個細節與自動生產的代碼沒有直接關聯,但是我們在99%的時候忽視了它。通常來說,當我們想要設計某個類是不可變類時,結果就會是這樣子:

  1. public class MyImmutableClass { 
  2.  
  3.     private final int id; 
  4.     private final String name; 
  5.  
  6.     public MyImmutableClass(int id, String name) { 
  7.         this.id = id; 
  8.         this.name = name; 
  9.     } 
  10.  
  11.     public int getId() { 
  12.         return id; 
  13.     } 
  14.  
  15.     public String getName() { 
  16.         return name; 
  17.     } 

 私有字段及最終字段,沒有setter方法,沒有構造函數初始化。OK,這很好,但是我們遺漏了什么。不妨看看如果我們這么做,會出現什么情況:

  1. public class MyImmutableDerivedClass extends MyImmutableClass{ 
  2.     public String address; 
  3.     public MyImmutableDerivedClass(int id,  
  4.                                    String name,  
  5.                                    String address) { 
  6.         super(id, name); 
  7.         this.address = address; 
  8.     } 
  9.     public String getAddress() { 
  10.         return address; 
  11.     } 
  12.     public void setAddress(String address) { 
  13.         this.address = address; 
  14.     } 

這第二個類的實例(繼承自MyImmutableClass)可以充當它的代理,客戶根本不注意,即:

  1. MyImmutableClass myImmutableClass  
  2.  
  3.                 = new MyImmutableDerivedClass(1, "name", "address");  

這并不理想,因為這個派生類不是不可變的! 解決辦法很簡單,我們不妨讓不可變類成為最終類:

  1. public final class MyImmutableClass {  
  2.  
  3.     //...  
  4.  
  5. }  

 現在就不可能創建派生類了。

 

getter和setter方法

我們常常創建擁有所有字段的類,并且在自動生成的代碼這股熱潮下,為所有字段添加構造函數、getter方法和setter方法。

我們果真需要這么做嗎?

  1. public class MyClass { 
  2.  
  3.     private Collaborator1 collaborator1; 
  4.     private Collaborator2 collaborator2; 
  5.  
  6.     public MyClass(Collaborator1 collaborator1, 
  7.                    Collaborator2 collaborator2) { 
  8.         this.collaborator1 = collaborator1; 
  9.         this.collaborator2 = collaborator2; 
  10.     } 
  11.  
  12.     public Collaborator1 getCollaborator1() { 
  13.         return collaborator1; 
  14.     } 
  15.  
  16.     public void setCollaborator1(Collaborator1 collaborator1) { 
  17.         this.collaborator1 = collaborator1; 
  18.     } 
  19.  
  20.     public Collaborator2 getCollaborator2() { 
  21.         return collaborator2; 
  22.     } 
  23.  
  24.     public void setCollaborator2(Collaborator2 collaborator2) { 
  25.         this.collaborator2 = collaborator2; 
  26.     } 

一個類應該暴露最少數量的內部細節。所以,前面說過一個類應該有包作用域,直到它需要是公共的,現在我要說除非需要,否則類不該有getter或setter方法。如果出現這種情況,就要想想這是不是***決定,或者你的設計中有沒有哪里是不合適的。

equals方法

通常來說,IDE暴露一起生成equals方法和hashCode方法的選項。這有必要,因為我們通常需要這兩種方法都有一種合適的、一致的實現(我在此不過多的具體解釋,假設我們都知道equals和hashCode契約)。

我不是很反對IDE為hashCode生成的實現,它們通常是***的,盡管我們可以進一步對它們加以優化。不過,equals實現方面存在一個小問題:

  1. public class MyDerivedClass extends MyClass { 
  2.     private final String address; 
  3.  
  4.     public MyDerivedClass(int id, String name, String address) { 
  5.         super(id, name); 
  6.         this.address = address; 
  7.     } 
  8.  
  9.     public String getAddress() { 
  10.         return address; 
  11.     } 
  12.  
  13.     // equals and hashCode 

 這里一切看起來沒問題,但是出現了嚴重的情況:這個類違反了里氏替換原則(Liskov Substitution Principle)。這個原則是SOLID原則的一部分;簡而言之,那就是“派生類可充當基類,客戶不會注意到它。”這個定義有點費勁(這個原則需要用一整篇文章來介紹),于是我會用一個例子來這個問題,那樣我們可以明白為何我們的equals方法有問題。

不妨創建一個派生類:

 

  1. public class MyDerivedClass extends MyClass { 
  2.     private final String address; 
  3.     public MyDerivedClass(int id, String name, String address) { 
  4.         super(id, name); 
  5.         this.address = address; 
  6.     } 
  7.     public String getAddress() { 
  8.         return address; 
  9.     } 
  10.     // equals and hashCode 

 

我忽略了equals和hashCode實現,因為它們對這個例子來說沒有必要。

 

  1. public static void main(String[] args) { 
  2.     MyClass object1 = new MyClass(1, "name"); 
  3.     MyDerivedClass object2 = new MyDerivedClass(1, "name", "address"); 
  4.     System.out.println(areEquals(object1, object2)); 
  5. public static boolean areEquals(MyClass object1, MyClass object2) { 
  6.     return object1.equals(object2); 

 

方法areEquals收到MyClass的兩個實例。從含有的信息來看,這兩個實例一模一樣,所以我們認為areEquals會返回true。但實則不然,因為自動生成的equals方法執行這個操作:

  1. if (o == null || 
  2.     getClass() != o.getClass()) 
  3.         return false; 

getClass為***個對象返回MyClass,為第二個對象返回MyDerivedClass,于是我們的方法equals返回false。

我們可以討論這種情況出現的頻率,因為兩者都是不同類的實例。但是,兩個字段中所有字段的值一模一樣,果真有意義嗎?實際上,areEquals的一種替代實現可能是這樣:

  1. public static boolean areEquals(MyClass object1, MyClass object2) { 
  2.     return object1.getId() == object2.getId() && 
  3.             object1.getName().equals(object2.getName()); 

 

在這里,結果是true。

這就是里氏替代原則的大致內容。這個原則的影響之一就是,如果我們覆蓋方法,它就會添加某個行為,而不清除基類執行的任何操作。顯著違反這個原則的是,覆蓋一個方法讓它是空的這種做法(我猜許多人干過這種事)。

于是,我們需要為方法equals提供一種更好的實現。它應該是這樣子:

 

  1. @Override 
  2. public boolean equals(Object o) { 
  3.     if (this == o) 
  4.         return true; 
  5.  
  6.     if (!(o instanceof MyClass)) 
  7.         return false; 
  8.  
  9.     MyClass myClass = (MyClass) o; 
  10.  
  11.     if (id != myClass.id) 
  12.         return false; 
  13.  
  14.     if (name != null ? 
  15.             !name.equals(myClass.name) : 
  16.             myClass.name != null) 
  17.                 return false; 
  18.  
  19.     return true; 

不是使用getClass,詢問對象知道其類,我們所做的而是,如果它是類的實例(equals駐留于運算符instanceof),要求對象由參數傳遞給equals。這個運算符為這個類及其派生類的實例返回true。現在,我們的areEquals方法終于按我們所需的方式來工作。

事實上,IntelliJ暴露了自動生成equals這個版本的一種方法,但是我們必須認真選擇向導程序的選項,因為它在默認情況下未勾選,我們必須自行勾選。它是“Accept subclasses as parameters to equals() method”:

使用自動代碼生成工具(IDE),要注意這些生成代碼的小瑕疵對話框本身提到,實現在默認情況下違反了equals契約。Instanceof實現似乎與一些框架不兼容。這種情況下,我們應該改用***個版本,但是始終要小心由此帶來的后果。所以,不妨一開始就使用正確的版本,必要時再改用B計劃。

原文標題:Review your auto-generated code

作者:Raúl Ávila

【51CTO譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】

 

責任編輯:陶家龍 來源: 51CTO
相關推薦

2009-07-16 11:40:23

ibatis自動生成abator

2022-05-17 08:26:04

API后端

2023-07-27 18:39:20

低代碼開發編碼

2013-09-03 09:30:44

軟件工程師軟件工程師頭銜

2020-03-19 15:02:53

Go語言學習

2012-07-24 13:36:58

運維

2025-06-10 01:34:00

效率工具編碼

2009-07-14 17:12:26

ibatis自動代碼生

2021-07-30 16:34:31

前端Nodejs開發

2013-06-07 13:30:20

2014-12-19 09:54:03

Java

2019-02-20 09:35:05

爬蟲工程師開發工具

2019-06-24 09:40:17

前端前端工程師開發工具

2023-09-15 08:00:20

2011-08-30 10:09:37

Java

2010-10-12 09:51:11

2021-01-27 10:35:30

漏洞

2010-04-25 15:29:58

Twitter可伸縮性

2023-12-28 09:54:22

Java內存開發

2019-06-23 16:02:12

Kubernetes集群節點高并發
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 天天操夜夜操 | 日韩综合网| 一区二区久久 | 久久综合久久自在自线精品自 | 欧美激情精品久久久久久变态 | 成人av一区二区三区 | 欧美日韩亚 | 天天操操操操操 | 国产精品久久久久久久模特 | 羞羞的视频免费看 | 成人福利网 | 精品日韩一区 | 色欧美片视频在线观看 | 国产情侣久久 | 欧美精品成人一区二区三区四区 | 狠狠爱视频 | 久久网一区二区 | 欧美一区二区三区久久精品 | 亚洲视频精品在线 | 在线免费小视频 | 国产乱码精品一区二三赶尸艳谈 | 欧美欧美欧美 | 欧美一区两区 | 99热.com| 国产精品福利视频 | 中日韩欧美一级片 | www.久久.com | 久久综合影院 | 欧美日韩看片 | 视频在线一区二区 | 国产成人小视频 | 99re6在线视频| 一区二区三区在线播放 | 日韩精品一区二区三区视频播放 | 精品国产乱码一区二区三区a | 久久久久久高潮国产精品视 | 精品在线观看一区 | 美国黄色毛片 | 最近中文字幕第一页 | 一区在线视频 | 欧美视频免费 |