ฉันสงสัยว่าอะไรคือความแตกต่างระหว่างการประกาศสองวิธีต่อไปนี้:
public Object doSomething(Object obj) {....}
public <T> T doSomething(T t) {....}
มีบางอย่างที่คุณสามารถ / จะทำกับสิ่งอื่นได้หรือไม่? ฉันไม่พบคำถามนี้จากที่อื่นในไซต์นี้
ฉันสงสัยว่าอะไรคือความแตกต่างระหว่างการประกาศสองวิธีต่อไปนี้:
public Object doSomething(Object obj) {....}
public <T> T doSomething(T t) {....}
มีบางอย่างที่คุณสามารถ / จะทำกับสิ่งอื่นได้หรือไม่? ฉันไม่พบคำถามนี้จากที่อื่นในไซต์นี้
คำตอบ:
แยกจากบริบท - ไม่มีความแตกต่าง ทั้งสองt
และobj
คุณสามารถเรียกใช้เฉพาะวิธีการของObject
.
แต่ด้วยบริบท - ถ้าคุณมีคลาสทั่วไป:
MyClass<Foo> my = new MyClass<Foo>();
Foo foo = new Foo();
แล้ว:
Foo newFoo = my.doSomething(foo);
รหัสเดียวกันกับวัตถุ
Foo newFoo = (Foo) my.doSomething(foo);
ข้อดีสองประการ:
Object
Foo
หากส่งคืนBar
คุณจะมีClassCastException
รันไทม์ความแตกต่างคือในตอนแรกเราระบุว่าผู้เรียกต้องส่งผ่านอินสแตนซ์ Object (คลาสใดก็ได้) และจะได้รับ Object อื่นกลับมา (คลาสใด ๆ ไม่จำเป็นต้องเป็นประเภทเดียวกัน)
ในประการที่สองประเภทที่ส่งคืนจะเป็นประเภทเดียวกับที่กำหนดเมื่อคลาสถูกกำหนด
Example ex = new Example<Integer>();
ที่นี่เราระบุประเภท T ที่จะช่วยให้เราบังคับใช้ข้อ จำกัด เพิ่มเติมเกี่ยวกับคลาสหรือวิธีการ ตัวอย่างเช่นเราสามารถสร้างอินสแตนซ์ a LinkedList<Integer>
หรือLinkedList<Example>
และเรารู้ว่าเมื่อเราเรียกหนึ่งในวิธีการเหล่านี้เราจะได้อินสแตนซ์จำนวนเต็มหรือตัวอย่างกลับคืนมา
เป้าหมายหลักที่นี่คือรหัสการเรียกสามารถระบุประเภทของวัตถุที่คลาสจะดำเนินการแทนการใช้ type-casting เพื่อบังคับใช้สิ่งนี้
ดูJava Generics * จาก Oracle
* อัปเดตลิงค์
ความแตกต่างคือด้วยวิธีการทั่วไปฉันไม่จำเป็นต้องแคสต์และฉันได้รับข้อผิดพลาดในการคอมไพล์เมื่อฉันทำผิด:
public class App {
public static void main(String[] args) {
String s = process("vv");
String b = process(new Object()); // Compilation error
}
public static <T> T process(T val) {
return val;
}
}
การใช้วัตถุฉันต้องแคสต์เสมอและฉันไม่ได้รับข้อผิดพลาดใด ๆ เมื่อฉันทำผิด:
public class App {
public static void main(String[] args) {
String s = (String)process("vv");
String b = (String)process(new Object());
}
public static Object process(Object val) {
return val;
}
}
คุณไม่จำเป็นต้องทำการแคสต์คลาสเพิ่มเติม ในกรณีแรกคุณจะได้รับวัตถุของคลาส java.lang.Object เสมอซึ่งคุณจะต้องส่งไปยังคลาสของคุณ ในกรณีที่สอง T จะถูกแทนที่ด้วยคลาสที่กำหนดไว้ในลายเซ็นทั่วไปและไม่จำเป็นต้องมีการแคสต์คลาส
ที่รันไทม์ไม่มีอะไรเลย แต่ในเวลาคอมไพล์ครั้งที่สองจะทำการตรวจสอบประเภทเพื่อให้แน่ใจว่าประเภทของพารามิเตอร์และประเภทของค่าที่ส่งคืนตรงกัน (หรือเป็นประเภทย่อยของ) ประเภทใดก็ตามที่ T แก้ไขได้ (ตัวอย่างแรกจะทำการตรวจสอบประเภทด้วย แต่ทุกออบเจ็กต์เป็น a ประเภทย่อยของ Object ดังนั้นทุกประเภทจะได้รับการยอมรับ)
T เป็นประเภททั่วไป ความหมายสามารถแทนที่ได้โดยอ็อบเจ็กต์ที่มีคุณสมบัติใด ๆ ในรันไทม์ คุณอาจเรียกใช้วิธีการดังต่อไปนี้:
String response = doSomething("hello world");
หรือ
MyObject response = doSomething(new MyObject());
หรือ
Integer response = doSomething(31);
อย่างที่คุณเห็นมีความหลากหลายอยู่ที่นี่
แต่ถ้ามีการประกาศให้ส่งคืน Object คุณจะไม่สามารถทำได้เว้นแต่คุณจะพิมพ์ cast things
<T>
ไม่มีการทำ autoboxing?
ในกรณีแรกจะใช้พารามิเตอร์ประเภท egstring และส่งคืนประเภท foo ในกรณีที่สองจะใช้พารามิเตอร์ประเภท foo และส่งคืนอ็อบเจ็กต์ประเภท foo
มีเหตุผลบางประการที่คุณสามารถพิจารณา Generics แทนประเภทวัตถุใน Java: