使用Java :: Geci生成setter和getter
在本文中 ,我們創建了非常簡單的hello-world生成器,以介紹框架以及通常如何生成生成器。 在本文中,我們將研究訪問器生成器,它是在Java :: Geci的核心模塊中定義的,它是商業級的,而不是僅用于演示的生成器。 即使生成器是商業級的,使用框架的服務,生成器也具有簡單的代碼,因此可以在文章中表示。
訪問器生成器有什么作用
訪問器是設置器和獲取器。 當一個類有很多字段并且我們希望幫助封裝時,我們將這些字段聲明為private字段并創建setter和getter,每個字段一對,可以設置該字段的值(setter)并獲取該字段的值(吸氣劑)。 請注意,與許多初中生認為的相反,創建setter和getter并不是單獨封裝,而是可能是進行正確封裝的工具。 同時請注意,它也可能不是正確封裝的工具。 您可以在“ Joshua Bloch:有效的Java第三版”項目16中了解更多信息。
仔細閱讀它。 該書說它是為Java 9更新的。該Java版本包含模塊系統。 Item 16一章中沒有提及,甚至該版本仍然說要使用帶有setter和getter的私有成員作為公共類,在Java 9的情況下,這也意味著模塊中不會導出的包中的類。
許多開發人員認為,設置者和獲取者本質上是邪惡的,并且是不良設計的標志。 不要犯錯! 他們不主張直接使用原始字段。 那會更糟。 他們認為您應該以更加面向對象的思維方式進行編程。 在我看來,它們是正確的,并且按照我的專業實踐,我必須使用很多類來維護舊版應用程序,這些類使用包含setter和getter的舊版框架來維護舊版應用程序,這些框架是應用程序周圍的編程工具所需的。 理論是一回事,現實生活是另一回事。 除非我們在添加新字段時忘記執行它們,否則不同的集成開發環境和許多其他工具(例如,生成器和獲取器)會為我們生成。
設置器是一種方法,其參數與字段的類型相同,并返回void 。 (Aka不返回任何值。)按照約定set的名稱,并使用首字母大寫的字段名稱。 對于字段businessOwner ,設置者通常是setBusinessOwner 。 設置器將字段的值設置為設置器的參數的值。
getter也是一種不帶任何參數但返回參數值的方法,因此它的返回類型與字段的類型相同。 按照慣例,getter的名稱是get ,再次是大寫的字段名稱。 這樣,獲取者將是getBusinessOwner 。
在的情況下, boolean或Boolean類型fiels吸氣劑具有is前綴,所以isBusinessOwner還可以的情況下,該領域是一些布爾類型的有效名稱。
訪問器會為其必需的所有字段生成setter和getter。
如何生成訪問器
訪問器生成器必須為該類的某些字段生成代碼。 該生成器是Java :: Geci中過濾字段生成器的理想候選者。 過濾后的字段生成器擴展了AbstractFilteredFieldsGenerator類,并且它的process()方法為每個過濾后的字段調用一次。 除了幾周前在文章中看到的常規Source和CompoundParams參數之外,該方法還將Field作為第三個參數。
類AbstractFilteredFieldsGenerator類使用配置參數filter來過濾字段。 這樣,對于擴展該類的每個生成器,要考慮的字段的選擇都是相同的,并且生成器不必關心字段過濾:它是為它們完成的。
生成器代碼的主要部分如下:
public class Accessor extends AbstractFilteredFieldsGenerator { ... @Override public void process(Source source, Class<?> klass, CompoundParams params, Field field) throws Exception { final var id = params.get( "id" ); source.init(id); var isFinal = Modifier.isFinal(field.getModifiers()); var name = field.getName(); var fieldType = GeciReflectionTools.typeAsString(field); var access = check(params.get( "access" , "public" )); var ucName = cap(name); var setter = params.get( "setter" , "set" + ucName); var getter = params.get( "getter" , "get" + ucName); var only = params.get( "only" ); try (var segment = source.safeOpen(id)) { if (!isFinal && ! "getter" .equals(only)) { writeSetter(name, setter, fieldType, access, segment); } if (! "setter" .equals(only)) { writeGetter(name, getter, fieldType, access, segment); } } } }省略號處的代碼包含更多方法,我們將在后面介紹。 第一個調用是獲取參數id 。 這是一個特殊參數,如果未定義,則默認params.get("id")返回是生成器的助記符。 這是唯一具有這樣的全局默認值的參數。
對source.init(id)的調用可確保即使生成器未向該段寫入任何內容,該段也將被視為“已觸摸”。 在某些情況下可能會發生這種情況,并且在編寫生成器時,對于生成器要寫入的任何段調用source.init(id)不會受到傷害。
該代碼查看實際字段以檢查該字段是否為最終字段。 如果該字段為final,則必須在創建對象時獲取值,此后,設置器將無法對其進行修改。 在這種情況下,將僅為該字段創建一個吸氣劑。
setter / getter生成器需要的下一項功能是字段名稱,以及字段類型的字符串表示形式。 靜態實用程序方法GeciReflectionTools.typeAsString()是框架中提供此功能的便捷工具。
可選的配置參數access將進入相同名稱的變量,并在setter和getter的access修飾符需要不同于public 。 默認值是public ,它被定義為方法params.get()的第二個參數。 方法check()是生成器的一部分。 它檢查該改性劑是正確并防止在大多數情況下產生的語法差錯代碼(例如:創建setter和吸氣與訪問修飾符pritected )。 我們將在一段時間后研究該方法。
接下來的事情是getter和setter的名稱。 默認情況下是set/get +字段的大寫名稱,但也可以由配置參數setter和getter定義。 這樣,如果絕對需要,您可以擁有isBusinessOwner 。
最后一個配置參數only是密鑰。 如果代碼指定only='setter'或only='getter'則僅生成setter或僅生成getter。
生成器要寫入的段在try-with-resources塊的開頭打開,然后調用本地writeSetter和writeGetter方法。 有兩種不同的方法可以從源對象打開細分。 一個正在調用open(id) ,另一個正在調用safeOpen(id) 。 第一種方法將嘗試打開該段,如果在類源文件中未定義名稱的段,則該方法將返回null 。 生成器可以檢查無效性,并且如果已編程,則可以使用其他段名稱。 另一方面,如果無法打開該段,則safeOpen()會引發GeciException 。 這是更安全的版本,以避免以后生成器中出現空指針異常。 不是很好。
請注意,僅當字段不是final且未將only配置鍵未配置為getter (僅)時,才寫入setter。
讓我們看一下這兩種方法。 畢竟,這些是真正生成代碼的生成器的真正核心方法。
private static void writeGetter(String name, String getterName, String type, String access, Segment segment) { segment.write_r(access + " " + type + " " + getterName + "(){" ) .write( "return " + name + ";" ) .write_l( "}" ) .newline(); } private static void writeSetter(String name, String setterName, String type, String access, Segment segment) { segment.write_r(access + " void " + setterName + "(" + type + " " + name + "){" ) .write( "this." + name + " = " + name + ";" ) .write_l( "}" ) .newline(); }這些方法獲取字段的名稱,訪問者的名稱,作為字符串的字段的類型,訪問修飾符字符串以及必須將代碼寫入的Segment 。 代碼生成器不會直接寫入源文件。 框架提供的segment對象用于發送生成的代碼,如果需要,框架會將編寫的行插入源代碼中。
該段的write() , write_l()和write_r()方法可用于編寫代碼。 如果有多個參數,它們的工作方式與String.format非常相似,但是它們也關心適當的制表。 當代碼調用write_r()該段將記住必須將其后的行列表在右邊的四個空格處。 當代碼調用write_l()該段知道制表必須減少四個字符(即使對于實際的寫入行也是如此)。 它們還處理多行字符串,以便將它們全部正確制成表格。
生成的代碼也應該可讀。
最終的重要方法是訪問修飾符檢查。
private static final Set<String> accessModifiers = Set.of( "public" , "private" , "protected" , "package" ); ... private String check( final String access) { if (!access.endsWith( "!" ) && !accessModifiers.contains(access)) { throw new GeciException( "'" +access+ "' is not a valid access modifier" ); } final String modifiedAccess; if ( access.endsWith( "!" )){ modifiedAccess = access.substring( 0 ,access.length()- 1 ); } else { modifiedAccess = access; } if ( modifiedAccess.equals( "package" )){ return "" ; } return modifiedAccess; }進行此檢查的目的是防止程序員錯誤地訪問access修飾符。 它檢查訪問修飾符是private (雖然我看不到這個的實際用例), protected , public還是package 。 最后一個轉換為空字符串,因為包保護的訪問是類方法的默認設置。 同時在配置中使用空字符串來表示程序包私有訪問實際上是不可讀的。
這樣,如果配置pritected包含拼寫錯誤的代碼,則代碼生成器將拋出異常,并拒絕生成已知包含語法錯誤的代碼。 另一方面,訪問修飾符也可以更復雜。 在極少數情況下,該程序可能需要同步的吸氣劑和吸氣劑。 我們不會試圖自動找出類似檢查字段是否可變的內容,因為這是邊界情況。 但是,生成器提供了克服有限語法檢查的可能性,并且僅提供任何字符串作為訪問修飾符即可。 如果訪問修飾符字符串以感嘆號結尾,則意味著使用生成器的程序員對訪問修飾符的正確性承擔全部責任,并且生成器將按原樣使用它(當然沒有感嘆號)。
剩下的是mnemonic和cap方法:
private static String cap(String s) { return s.substring( 0 , 1 ).toUpperCase() + s.substring( 1 ); } @Override public String mnemonic() { return "accessor" ; }框架使用mnemonic()方法來識別需要此生成器服務的源,并將其用作配置參數id的默認值。 所有生成器都應提供此功能。 另一種是cap的是大寫的字符串。 我不會解釋它是如何工作的。
樣品使用
@Geci ( "accessor filter='private | protected'" ) public class Contained1 { public void callMe() { } private final String apple = "" ; @Geci ( "accessors only='setter'" ) private int birnen; int packge; @Geci ( "accessor access='package' getter='isTrue'" ) protected boolean truth; @Geci ( "accessor filter='false'" ) protected int not_this; public Map<String,Set<Map<Integer,Boolean>>> doNothingReally( int a, Map b, Set<Set> set){ return null ; } //<editor-fold id="accessor" desc="setters"> //</editor-fold> }該類使用Geci注釋進行注釋。 參數為accessor filter='private | protected' accessor filter='private | protected' ,定義將在此源文件上使用的生成器的名稱并配置過濾器。 它說,我們需要私有和受保護字段的設置者和獲取者。 邏輯表達式應為:“過濾字段是私有的還是受保護的”。
一些字段也有注釋。 birnen將得到的只是一個二傳手, truth setter和getter將被封裝保護劑和吸氣將被命名為isTrue() 字段not_this不會獲得setter或getter,因為在字段注釋中覆蓋了過濾器表達式,并說: false永遠不會為true ,生成器需要對其進行處理。
未注釋字段apple ,它將根據類級別的配置進行處理。 它是私有的,因此將獲得訪問器,并且由于它是final因此將僅獲得吸氣劑。
之間的代碼
// <editor- fold id = "accessor" desc= "setters" > // < /editor-fold >將包含生成的代碼。 (您必須運行代碼才能看到它,我沒有在這里復制它。)
摘要
在本文中,我們研究了一個生成器,它是Java :: Geci框架中真實的商業級生成器。 遍歷代碼,我們討論了代碼的工作方式,還討論了編寫代碼生成器的其他一些更一般的方面。 下一步是使用Java :: Geci作為測試依賴項來啟動一個項目,使用訪問器生成器而不是IDE代碼生成器(這使您忘記重新執行setter getter生成),然后也許可以創建您的擁有自己的生成器,而不僅僅是setter和getter。
翻譯自: https://www.javacodegeeks.com/2019/06/generating-setters-and-getters-using-javageci.html
總結
以上是生活随笔為你收集整理的使用Java :: Geci生成setter和getter的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 华为mate20x值得买吗
- 下一篇: ai如何旋转画布_怎样使用AI旋转工具