สิ่งที่เรารู้คือ " อินสแตนซ์ทั้งหมดของคลาสใด ๆ จะใช้ออบเจ็กต์ java.lang.Class เดียวกันของประเภทคลาสนั้น "
เช่น)
Student a = new Student();
Student b = new Student();
จากนั้นa.getClass() == b.getClass()
เป็นจริง
ทีนี้สมมติ
Teacher t = new Teacher();
ไม่มีข้อมูลทั่วไปด้านล่างเป็นไปได้
Class studentClassRef = t.getClass();
แต่นี่มันผิดตอนนี้ .. ?
เช่น) public void printStudentClassInfo(Class studentClassRef) {}
สามารถเรียกได้ด้วยTeacher.class
สิ่งนี้สามารถหลีกเลี่ยงได้โดยใช้ยาชื่อสามัญ
Class<Student> studentClassRef = t.getClass(); //Compilation error.
ตอนนี้ T คืออะไร? T คือพารามิเตอร์ประเภท (เรียกอีกอย่างว่าตัวแปรประเภท); คั่นด้วยวงเล็บเหลี่ยม (<>) ตามหลังชื่อคลาส
T เป็นเพียงสัญลักษณ์เช่นชื่อตัวแปร (สามารถเป็นชื่อใดก็ได้) ที่ประกาศในระหว่างการเขียนไฟล์คลาส หลังจากนั้น T จะถูกแทนที่ด้วย
ชื่อคลาสที่ถูกต้องในระหว่างการเริ่มต้น ( HashMap<String> map = new HashMap<String>();
)
เช่น) class name<T1, T2, ..., Tn>
ดังนั้นClass<T>
แสดงถึงวัตถุคลาสของประเภทคลาสเฉพาะ ' T
'
สมมติว่าวิธีการเรียนของคุณต้องทำงานกับพารามิเตอร์ประเภทที่ไม่รู้จักเช่นด้านล่าง
/**
* Generic version of the Car class.
* @param <T> the type of the value
*/
public class Car<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
ที่นี่ T สามารถใช้เป็นString
ประเภทCarName
หรือเสื้อที่สามารถใช้เป็นInteger
ชนิดเป็นmodelNumber ,
หรือเสื้อที่สามารถใช้เป็นObject
ชนิดที่เป็นเช่นรถที่ถูกต้อง
ตอนนี้ที่นี่ข้างต้นเป็น POJO ง่าย ๆ ซึ่งสามารถใช้แตกต่างกันในขณะทำงาน
คอลเลกชันเช่น) รายการตั้งค่า Hashmap เป็นตัวอย่างที่ดีที่สุดซึ่งจะทำงานกับวัตถุที่แตกต่างกันตามการประกาศของ T แต่เมื่อเราประกาศ T เป็นสตริง
เช่น) HashMap<String> map = new HashMap<String>();
จากนั้นจะยอมรับเฉพาะวัตถุอินสแตนซ์สตริงคลาสเท่านั้น
วิธีการทั่วไป
วิธีการทั่วไปเป็นวิธีการที่แนะนำพารามิเตอร์ประเภทของตัวเอง สิ่งนี้คล้ายกับการประกาศชนิดทั่วไป แต่ขอบเขตของพารามิเตอร์ชนิดถูก จำกัด เฉพาะวิธีที่มีการประกาศ อนุญาตให้ใช้วิธีทั่วไปแบบคงที่และไม่คงที่ได้เช่นเดียวกับตัวสร้างคลาสทั่วไป
ไวยากรณ์สำหรับวิธีการทั่วไปรวมถึงพารามิเตอร์ประเภทภายในวงเล็บมุมและปรากฏขึ้นก่อนที่จะกลับมาประเภทของวิธีการ สำหรับวิธีการทั่วไปส่วนพารามิเตอร์ประเภทจะต้องปรากฏก่อนที่ประเภทการคืนสินค้าของวิธีการ
class Util {
// Generic static method
public static <K, V, Z, Y> boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
}
class Pair<K, V> {
private K key;
private V value;
}
นี่<K, V, Z, Y>
คือการประกาศประเภทที่ใช้ในการโต้แย้งวิธีที่ควรก่อนที่จะกลับมาซึ่งเป็นประเภทที่boolean
นี่
ในด้านล่าง; การประกาศประเภท<T>
ไม่จำเป็นต้องใช้ในระดับวิธีเนื่องจากมันมีการประกาศในระดับชั้นแล้ว
class MyClass<T> {
private T myMethod(T a){
return a;
}
}
แต่ด้านล่างผิดเนื่องจากพารามิเตอร์ประเภทระดับคลาส K, V, Z และ Y ไม่สามารถใช้ในบริบทแบบสแตติก (เมธอดสแตติกที่นี่)
class Util <K, V, Z, Y>{
// Generic static method
public static boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
}
สถานการณ์ที่ถูกต้องอื่น ๆ
class MyClass<T> {
//Type declaration <T> already done at class level
private T myMethod(T a){
return a;
}
//<T> is overriding the T declared at Class level;
//So There is no ClassCastException though a is not the type of T declared at MyClass<T>.
private <T> T myMethod1(Object a){
return (T) a;
}
//Runtime ClassCastException will be thrown if a is not the type T (MyClass<T>).
private T myMethod1(Object a){
return (T) a;
}
// No ClassCastException
// MyClass<String> obj= new MyClass<String>();
// obj.myMethod2(Integer.valueOf("1"));
// Since type T is redefined at this method level.
private <T> T myMethod2(T a){
return a;
}
// No ClassCastException for the below
// MyClass<String> o= new MyClass<String>();
// o.myMethod3(Integer.valueOf("1").getClass())
// Since <T> is undefined within this method;
// And MyClass<T> don't have impact here
private <T> T myMethod3(Class a){
return (T) a;
}
// ClassCastException for o.myMethod3(Integer.valueOf("1").getClass())
// Should be o.myMethod3(String.valueOf("1").getClass())
private T myMethod3(Class a){
return (T) a;
}
// Class<T> a :: a is Class object of type T
//<T> is overriding of class level type declaration;
private <T> Class<T> myMethod4(Class<T> a){
return a;
}
}
และในที่สุดวิธีการแบบคงที่ต้องการ<T>
การประกาศที่ชัดเจนเสมอ Class<T>
มันเป็นนิสัยมาจากระดับชั้น นี่เป็นเพราะระดับ Class T ถูกผูกไว้กับอินสแตนซ์
อ่านข้อ จำกัด ของ Generics ด้วย
สัญลักษณ์แทนและการพิมพ์ย่อย
อาร์กิวเมนต์ประเภทสำหรับวิธีการทั่วไป