เรามักจะเห็นสิ่งต่าง ๆ เช่น "@Override", "@Target" และอื่น ๆ ในรหัส Java สิ่งเหล่านี้คืออะไร?
ใน Java พวกเขาเป็น "คำอธิบายประกอบ"
ต่อไปนี้เป็นคำอธิบายของสารานุกรม Baidu: java.lang.annotation.rentention สามารถสั่งให้คอมไพเลอร์รักษาคำอธิบายประกอบที่กำหนดเองของคุณเมื่อคุณกำหนดรูปแบบคำอธิบายประกอบ คอมไพเลอร์จะทิ้งข้อมูลคำอธิบายประกอบไว้ในคลังเก็บของคลาส แต่ไม่ได้อ่านโดยเครื่องเสมือน แต่ให้ข้อมูลเมื่อโปรแกรมคอมไพเลอร์หรือเครื่องมือทำงานเท่านั้น
กล่าวอีกนัยหนึ่งคำอธิบายประกอบจะขึ้นอยู่กับไฟล์คลาสและมีผลเช่นเดียวกับแมโครในภาษา C
ไม่มีร่องรอยของคำอธิบายประกอบในไฟล์คลาส
พื้นฐานของคำอธิบายประกอบคือการสะท้อน ดังนั้นคำอธิบายประกอบจึงสามารถเข้าใจได้ว่าเป็นแนวคิดที่ไม่ซ้ำกันกับ Java
1. เมตาโน้ต
ในแพ็คเกจ java.lang.annotation มีการกำหนด "primitives" สี่รายการของคำอธิบายประกอบ
1).@เป้าหมายใช้เพื่อชี้แจงประเภทที่ถูกแก้ไข: (วิธีการ, ฟิลด์, คลาส, อินเทอร์เฟซ ฯลฯ )
2).@การเก็บรักษาอธิบายการมีอยู่ของการรวม:
คำอธิบายประกอบการ REATINGINTPOLICY.RUNTIME จะมีอยู่ในไฟล์คลาส Bytecode และสามารถรับได้ผ่านการสะท้อนกลับที่รันไทม์
RetentionPolicy.Class นโยบายการเก็บข้อมูลเริ่มต้นคำอธิบายประกอบจะมีอยู่ในไฟล์คลาสไบต์ แต่ไม่สามารถรับได้ในระหว่างการรันไทม์
REATINGINGPOLICY.Source คำอธิบายประกอบมีอยู่ในซอร์สโค้ดเท่านั้นและไม่มีอยู่ในไฟล์คลาสไบต์
3).@เอกสารโดยค่าเริ่มต้นคำอธิบายประกอบจะไม่ถูกบันทึกใน Javadoc แต่คำอธิบายประกอบนี้สามารถใช้เพื่อระบุว่าต้องบันทึกคำอธิบายประกอบนี้
4). @meta-annotation ที่สืบทอดมาเป็นคำอธิบายประกอบแท็กและ @inherited อธิบายว่ามีการสืบทอดประเภทที่มีคำอธิบายประกอบ
หากมีการใช้ประเภทคำอธิบายประกอบที่ใช้ @Inherited Modification สำหรับชั้นเรียนคำอธิบายประกอบจะถูกใช้สำหรับคลาสย่อยของคลาส
2. คำอธิบายประกอบแบบกำหนดเอง
แพ็คเกจ com.joyfulmath.jvmexample.annnotaion; นำเข้า java.lang.annotation.elementtype; นำเข้า java.lang.annotation.retention; นำเข้า java.lang.notation.retentionpolicy; 13:36*/@Target (ElementType.field) @Retention (RETINGINTPOLICY.RUNTIME) สาธารณะ @Interface FruitName {ค่าสตริง () ค่าเริ่มต้น "";} ก่อนอื่นคำอธิบายประกอบโดยทั่วไปต้องมีการปรับเปลี่ยนคำอธิบายประกอบเมตา 2 รายการ:
@Target (ElementType.field)
@Retention (RetentionPolicy.runtime)
ฟังก์ชั่นเฉพาะได้รับการอธิบายข้างต้น
คำอธิบายประกอบทั้งหมดจะมีส่วนที่คล้ายกับ "func" สิ่งนี้สามารถเข้าใจได้ว่าเป็นพารามิเตอร์คำอธิบายประกอบ
แพ็คเกจ com.joyfulmath.jvmexample.annnotaion; นำเข้า com.joyfulmath.jvmexample.tracelog;/*** @author deman.lu*@version ใน 2016-05-23 13: 37*/แอปเปิ้ลระดับสาธารณะ displayapplename () {tracelog.i (applename);}} บันทึกของรหัสนี้:
05-23 13: 39: 38.780 26792-26792/com.joyfulmath.jvmexample I/Apple: DisplayApplename: NULL [AT (Apple.java:16)]]
เหตุใดการมอบหมายจึงไม่ประสบความสำเร็จ? จะกำหนดไฟล์ให้กับคำอธิบายประกอบ "Apple" ได้อย่างไร? จะทำอย่างไรถ้าคอมไพเลอร์ยังไม่รู้
3. ตัวประมวลผลคำอธิบายประกอบ
นอกจากนี้เรายังต้องการโปรเซสเซอร์เพื่ออธิบายว่าคำอธิบายประกอบทำงานอย่างไรไม่เช่นนั้นจะเกือบจะเหมือนกับคำอธิบายประกอบ
ผ่านการสะท้อนกลับสามารถรับเนื้อหาคำอธิบายประกอบได้:
แพ็คเกจ com.joyfulmath.jvmexample.annotaion; นำเข้า com.joyfulmath.jvmexample.tracelog; นำเข้า java.lang.reflect.field;/*** @author deman.lu*@version ใน 2016-05-23 14: 08* fruitnamestr = ""; ฟิลด์ [] ฟิลด์ = clazz.getDeclaredFields (); สำหรับ (ฟิลด์ฟิลด์: ฟิลด์) {ถ้า (field.isannotationpresent (fruitname.class)) {fruitname fruitname = field.getannotation (fruitname.class);นี่คือการใช้คำอธิบายประกอบทั่วไป
การวิเคราะห์กรอบคำอธิบายประกอบ Android
ดังที่เห็นได้จากด้านบนการใช้เฟรมเวิร์กคำอธิบายประกอบเป็นหลักต้องมีการสะท้อน
แต่ถ้าฉันใช้เฟรมเวิร์กคำอธิบายประกอบกับฟังก์ชั่นของการสะท้อนกลับฉันก็อาจใช้มันโดยตรงมันง่ายกว่า
หากมีกลไกก็สามารถหลีกเลี่ยงการเขียนรหัสที่คล้ายกันได้มากโดยเฉพาะอย่างยิ่งในระหว่างการพัฒนา Android จำนวน FindViewByID และ onClick จำนวนมากและเหตุการณ์อื่น ๆ นั้นสอดคล้องกัน
รูปแบบรหัสมีความสอดคล้องกัน แต่รหัสแตกต่างกัน ในเวลานี้การใช้กรอบคำอธิบายประกอบสามารถประหยัดเวลาในการพัฒนาได้มากและแน่นอนว่าจะเพิ่มค่าใช้จ่ายอื่น ๆ ตามลำดับ
นี่คือตัวอย่างของการใช้ Butterknife:
@bindstring (r.string.login_error)
String LoginERRORMESSAGE;
ดูเหมือนง่ายมากคือการกำหนดสตริงให้กับค่าเริ่มต้นที่สอดคล้องกับสตริง Res วิธีนี้คุณสามารถประหยัดเวลาได้ แน่นอนว่านี่เป็นเพียงตัวอย่าง
หากคุณใช้คำอธิบายประกอบอื่น ๆ อย่างหนักคุณสามารถประหยัดเวลาในการพัฒนาได้มาก
มาดูกันว่ามันถูกนำไปใช้อย่างไร:
Package Butterknife; นำเข้า Android.support.annotation.stringres; นำเข้า java.lang.annotation.retention; นำเข้า Java.lang.annotation.target; นำเข้า java.lang.lang.lang.elementtype.efioldation; <pre> <code>* {@literal @} bindString (r.string.username_error) สตริงผู้ใช้ชื่อผู้ใช้งาน;* </code> </pre>*/ @การเก็บรักษา (คลาส) @Target (ฟิลด์) public @interface bindstring {/** รหัสทรัพยากรสตริง */@stringRes ค่า int ();} BindString มีพารามิเตอร์เพียงหนึ่งค่านั่นคือการกำหนดค่าให้ @StringRes
เช่นเดียวกับข้างต้นข้างต้นคือที่กำหนดและใช้คำอธิบายประกอบ แต่คำอธิบายที่แท้จริงของคำอธิบายประกอบมีดังนี้: butterknifeprocessor
แผนที่ส่วนตัว <การพิมพ์, bindingclass> findandParsetargets (Roundenvironment Env)
ฟังก์ชั่นนี้สกัดรหัสบางส่วน:
// ประมวลผลแต่ละ @bindstring element.for (องค์ประกอบองค์ประกอบ: env.getElementsAnnotated ด้วย (bindstring.class)) {ถ้า (! superficialValidation.validatelement (องค์ประกอบ)) ดำเนินการต่อ; ลอง {parseresourcestring ค้นหาองค์ประกอบทั้งหมดของคำอธิบายประกอบการเชื่อมโยงและเริ่มวิเคราะห์:
Private Void ParseresourCestring (องค์ประกอบองค์ประกอบ, แผนที่ <typeElement, bindingClass> targetClassMap, ตั้งค่า <ypeelement> eRasedTargetNames) {boolean haserror = false; typeeelement enclosingElement = (พิมพ์) องค์ประกอบ (! string_type.equals (element.astype (). toString ())) {ข้อผิดพลาด (องค์ประกอบ, "ประเภทฟิลด์@%s ต้องเป็น 'สตริง' (%s.%s)", bindstring.class.getSimplename ข้อ จำกัด . haserror | = isinaccessibleviagieneratedCode (bindstring.class, "ฟิลด์", องค์ประกอบ); haserror | = isbindinginwrongpackage (bindstring.class, องค์ประกอบ); ถ้า (haserror) {return; element.getNanotation (bindString.class) .Value (); bindingClass bindingClass = getOrCreateTargetClass (TargetClassMap, EnclosingElement); FieldResourceBinding การเชื่อมโยง = ใหม่ FieldResourceBinding (ID, ชื่อ, false); bindingclass.addresource (binding); ErasedTargetNames.add (EnclosingElement);} ก่อนอื่นตรวจสอบว่าองค์ประกอบเป็นประเภทสตริงหรือไม่
// รวบรวมข้อมูลบน field.string name = element.getSimplename (). toString (); int id = element.get.getAnnotation (bindstring.class) .Value ();
รับชื่อของฟิลด์และรหัสสตริง
สุดท้าย
แผนที่ <การพิมพ์, bindingClass> targetClassMap
คำอธิบายองค์ประกอบและคำอธิบายประกอบถูกเก็บไว้ทีละหนึ่งในแผนที่
@Override กระบวนการบูลีนสาธารณะ (set <? ขยายการพิมพ์> องค์ประกอบ, envolenvironment env) {แผนที่ <การพิมพ์, bindingClass> targetClassMap = findandParsetargets (env); สำหรับ (map.entry <typeeelement entry.getValue (); ลอง {bindingclass.brewjava (). writeto (filer);} catch (ioexception e) {ข้อผิดพลาด (typeelement, "ไม่สามารถเขียนมุมมอง Binder สำหรับ type %s: %s", typelement, e.getMessage (); นี่คือที่เริ่มต้นกรอบคำอธิบายประกอบซึ่งเป็นกระบวนการอิสระ บทความนี้จะไม่ศึกษารายละเอียดเฉพาะเพียงชัดเจนนี่คือสถานที่ที่ขับเคลื่อนด้วยกรอบ
จากข้อมูลข้างต้นข้อมูลคำอธิบายประกอบทั้งหมดจะถูกเก็บไว้ใน TargetClassMap
รหัสที่ทำเครื่องหมายสีแดงด้านบนควรเป็นแกนหลักของกรอบคำอธิบายประกอบ
ตั้งแต่ Java SE5 Java ได้แนะนำ APT Tool ซึ่งสามารถอธิบายคำอธิบายประกอบล่วงหน้าได้ Java SE6 รองรับหน่วยประมวลผลคำอธิบายประกอบที่ขยายออกไป
และประมวลผลหลายครั้งในระหว่างการรวบรวม เราสามารถใช้โปรเซสเซอร์คำอธิบายประกอบแบบกำหนดเองเพื่อสร้างรหัส Java ใหม่ตามกฎเมื่อรวบรวม Java
Javafile Brewjava () {typespec.builder result = typespec.classBuilder (generatedClassName) .addModifiers (สาธารณะ); if (isfinal) {result.addModifiers (modifier.final);} else {result.addTypEvariable (typeVariablename.get ("t", targetTypename));} typename targetType = isfinal? TargetTypename: typeVariablename.get ("t"); ถ้า (hasparentbinding ()) {result.superclass (parameterizedTypename.get (parentbinding.generatedClassName, targetType));} else {result.addsuperinterface if (iSgeneratingunbinder ()) {result.addType (createunbinderclass (targetType));} อื่นถ้า (! isfinal) {result.addmethod (createBindTotargetMethod ());} return javafile.builder รหัสจากมีดเนย กุญแจสำคัญในข้อความนี้คือการสร้างไฟล์ใหม่
จากนั้นเขียนเนื้อหาที่เกี่ยวข้อง