结果集 tostring_关于避免对toString()结果进行解析或基于逻辑的美德
結果集 tostring
使用Java或我使用過的其他編程語言,我發現有時候可以用該語言完成某些事情,但通常不應該這樣做。 通常,這些誤用語言似乎無害,當開發人員首次使用它們時可能是有益的,但后來同一位開發人員或另一位開發人員遇到了相關的問題,需要克服或改變這些代價。 一個示例(也是本博客文章的主題)是使用Java中toString()調用的結果進行邏輯選擇或對其內容進行解析。
在2010年,我用Java語言編寫了toString()注意事項 ,當toString()方法可明確用于類時以及當它們包含該類對象的相關公共狀態時,我通常會首選它。 我仍然有這種感覺。 但是,我希望toString()實現足以使人們通過記錄的語句或調試器讀取對象的內容,而不是要由代碼或腳本解析的內容。 使用toString()方法返回的String進行任何類型的條件或邏輯處理都非常脆弱。 同樣,解析toString()返回的String以獲取有關實例狀態的詳細信息也是脆弱的。 我警告過(甚至是無意間)要求開發人員在前面提到的博客文章中解析toString()結果。
開發人員可能出于多種原因選擇更改toString()的生成的String,包括將現有字段添加到以前可能未表示過的輸出中,將更多數據添加到已經表示過的現有字段中,為新添加的字段添加文本,刪除不再在課程中的字段的表示形式或出于美學原因更改格式。 開發人員還可以更改toString()生成的String拼寫和語法問題。 如果toString()提供的String僅由人類在日志消息中分析對象的狀態時使用,則除非它們刪除了實質信息,否則這些更改不太可能成為問題。 但是,如果代碼依賴于整個String或為某些字段解析String ,則可以通過這些類型的更改輕松地將其破壞。
出于說明目的,請考慮以下Movie類的初始版本:
package dustin.examples.strings;/*** Motion Picture, Version 1.*/ public class Movie {private String movieTitle;public Movie(final String newMovieTitle){this.movieTitle = newMovieTitle;}public String getMovieTitle(){return this.movieTitle;}@Overridepublic String toString(){return this.movieTitle;} }在這個簡單且有些人為的示例中,只有一個屬性,因此類的toString()僅僅返回該類的單個String屬性作為類的表示形式并不稀奇。
下一個代碼清單包含一個不幸的決定(第22-23行),該決定基于Movie類的toString()方法的邏輯。
/*** This is a contrived class filled with some ill-advised use* of the {@link Movie#toString()} method.*/ public class FavoriteMoviesFilter {private final static List<Movie> someFavoriteMovies;static{final ArrayList<Movie> tempMovies = new ArrayList<>();tempMovies.add(new Movie("Rear Window"));tempMovies.add(new Movie("Pink Panther"));tempMovies.add(new Movie("Ocean's Eleven"));tempMovies.add(new Movie("Ghostbusters"));tempMovies.add(new Movie("Taken"));someFavoriteMovies = Collections.unmodifiableList(tempMovies);}public static boolean isMovieFavorite(final String candidateMovieTitle){return someFavoriteMovies.stream().anyMatch(movie -> movie.toString().equals(candidateMovieTitle));} }盡管有多個電影共用同一標題 ,但盡管存在一些潛在的問題,但該代碼似乎仍然可以工作一段時間。 但是,即使在遇到這些問題之前,如果開發人員確定他或她想將Movie.toString()表示形式的格式更改為下一個顯示的內容,則可能會意識到在相等性檢查中使用toString()的風險。代碼清單。
@Override public String toString() {return "Movie: " + this.movieTitle; }也許更改了Movie.toString()返回值,以使提供的String與Movie類的實例相關聯更加清楚。 不管進行更改的原因如何,以前列出的在影片標題上使用相等性的代碼現在都已損壞。 該代碼需要更改為使用contains而不是equals ,如下面的代碼清單所示。
public static boolean isMovieFavorite(final String candidateMovieTitle) {return someFavoriteMovies.stream().anyMatch(movie -> movie.toString().contains(candidateMovieTitle)); }當意識到Movie類需要更多信息來使電影與眾不同時,開發人員可以將發行年份添加到movie類。 接下來顯示新的Movie類。
package dustin.examples.strings;/*** Motion Picture, Version 2.*/ public class Movie {private String movieTitle;private int releaseYear;public Movie(final String newMovieTitle, final int newReleaseYear){this.movieTitle = newMovieTitle;this.releaseYear = newReleaseYear;}public String getMovieTitle(){return this.movieTitle;}public int getReleaseYear(){return this.releaseYear;}@Overridepublic String toString(){return "Movie: " + this.movieTitle;} }添加發行年份有助于區分具有相同標題的電影。 這也有助于將翻拍與原作區分開。 但是,無論電影發行的年份如何,使用Movie類查找收藏夾的代碼仍將顯示所有具有相同標題的電影。 換句話說,1960年版的《 海洋十一人》 ( 目前對IMDB評分為6.6 )將與2001年版的《 海洋十一人》 ( 目前對IMDB評分為7.8 )一起成為人們的最愛,盡管我更喜歡較新的版本。 同樣,1988年提出為電視版后窗 (的5.6評級目前IMDB )將返回為收藏旁邊的1954年版后窗 (執導的阿爾弗雷德·希區柯克 ,主演詹姆斯·斯圖爾特和格蕾絲·凱莉 ,以及額定8.5目前在IMDB中 ),盡管我更喜歡舊版本。
我認為toString()實現通常應包含對象的所有公共可用細節。 但是,即使將Movie的toString()方法增強為包括發行年份,客戶端代碼也不會基于年份進行區分,因為它僅對電影標題執行contain 。
@Override public String toString() {return "Movie: " + this.movieTitle + " (" + this.releaseYear + ")"; }上面的代碼顯示了添加到Movie的toString()實現中的發行年份。 下面的代碼顯示了如何更改客戶以正確遵守發布年份。
public static boolean isMovieFavorite(final String candidateMovieTitle,final int candidateReleaseYear) {return someFavoriteMovies.stream().anyMatch(movie -> movie.toString().contains(candidateMovieTitle)&& movie.getReleaseYear() == candidateReleaseYear); }我想情況下它是一個解析一個好主意,這是很難toString()上的結果,方法或基礎條件或其他邏輯toString()方法。 在我考慮的幾乎所有示例中,都有更好的方法。 在上面的示例中,最好向Movie添加equals() (和hashCode() )方法,然后對Movie實例使用相等性檢查,而不要使用單個屬性。 如果確實需要比較各個屬性(例如,在不需要對象相等且只需要一個或兩個字段相等的情況下),則可以使用適當的getXXX方法。
作為一名開發人員,如果我希望類的用戶(通常會最終包括我自己)不需要解析toString()結果或依賴于某個結果,則需要確保我的類使toString()提供任何有用的信息toString()可從其他易于訪問且更編程友好的資源中獲得,例如“獲取”方法以及相等性和比較方法。 如果開發人員不想通過公共API公開某些數據,則很可能開發人員也可能真的不想在返回的toString()結果中公開數據。 Joshua Bloch ( Effective Java)以粗體強調該文本,“……提供對toString()返回的值中包含的所有信息的編程訪問。”
在Effective Java中 ,Bloch還包括有關toString()方法是否應具有其提供的String表示形式的公告格式的討論。 他指出,這種表示形式(如果進行廣告宣傳)必須是從那時起一直使用的,如果它是廣泛使用的類,則可以避免我在本文中演示的運行時中斷類型。 他還建議,如果不能保證格式保持不變,則Javadoc也應包含與此相關的聲明。 通常,由于Javadoc和其他注釋經常比我想要的更被忽略,并且由于所宣傳的toString()表示形式具有“永久性”,因此我寧愿不依賴于toString()提供客戶端所需的特定格式,而是提供一種專用于客戶可以調用的方法。 這使我可以靈活地在類更改時更改toString() 。
JDK中的示例說明了我的首選方法,還說明了將特定格式指定為toString()的早期版本的危險。 BigDecimal的toString()表示在JDK 1.4.2和Java SE 5之間進行了更改,如“ J2SE 5.0中的不兼容性(自1.4.2起) ”所述:“ J2SE 5.0 BigDecimal的toString()方法的行為與早期版本不同版本。” BigDecimal.toString()的1.4.2版本的Javadoc僅在方法概述中聲明:“返回此BigDecimal的字符串表示形式。 使用Character.forDigit(int,int)提供的數字到字符的映射。 前導減號用于表示符號,小數點右邊的位數用于表示刻度。 (此表示形式與(字符串)構造函數兼容。)” Java SE 5和更高版本中BigDecimal.toString()的相同方法概述文檔更加詳細。 這樣冗長的描述,在此不再贅述。
當BigDecimal.toString()是與Java SE 5改變 ,其他的方法被引入本不同String表示: toEngineeringString()和toPlainString() 。 新引入的方法toPlainString()提供了JDK 1.4.2提供的BigDecimal的toString() 。 我傾向于提供提供特定String表示形式和格式的方法,因為這些方法可以具有其名稱中描述的格式的細節,并且Javadoc注釋以及對類的更改和添加不會像對它們產生影響那樣對這些方法產生影響一般的toString()方法。
有一些簡單的類可能適合原始實現的toString()方法將一勞永逸地修復且“永遠不會”改變的情況。 那些可能是解析返回字符串或在基礎邏輯候選String ,但即使在這種情況下,我更喜歡提供一種具有廣告和有保證的格式的另一種方法和離開toString()表示一些靈活性變化。 擁有多余的方法沒什么大不了的,因為盡管它們返回相同的內容,但多余的方法可以僅僅是調用toString的單行方法。 然后,如果toString()確實發生了變化,則可以將調用方法的實現更改為以前提供的toString() ,并且該額外方法的任何用戶都不會看到任何更改。
當將toString()結果解析為邏輯或基于toString()調用的結果邏輯時,最有可能在將特定方法視為客戶訪問特定數據的最簡單方法時進行。 最好通過其他特定的公共可用方法來使數據可用,并且類和API設計人員可以通過確保toString()提供的String中甚至可能有用的任何數據也可以通過編程訪問的特定替代方式來提供幫助。方法。 簡而言之,我的首選是將toString()一種方法,以查看有關表示形式中實例的一般信息,該實例可能會發生更改,并為表示形式中的特定數據段提供特定的方法,這些數據的更改可能性較小且更容易以編程方式訪問決策并基于可能需要特定于格式的解析的大型String進行決策。
翻譯自: https://www.javacodegeeks.com/2016/05/virtues-avoiding-parsing-basing-logic-tostring-result.html
結果集 tostring
總結
以上是生活随笔為你收集整理的结果集 tostring_关于避免对toString()结果进行解析或基于逻辑的美德的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java10个基础错误_我们处理了10亿
- 下一篇: 简述ddos常用攻击手段有哪些(简述DD