Java generics T vs Object


127

ฉันสงสัยว่าอะไรคือความแตกต่างระหว่างการประกาศสองวิธีต่อไปนี้:

public Object doSomething(Object obj) {....}

public <T> T doSomething(T t) {....}

มีบางอย่างที่คุณสามารถ / จะทำกับสิ่งอื่นได้หรือไม่? ฉันไม่พบคำถามนี้จากที่อื่นในไซต์นี้

คำตอบ:


112

แยกจากบริบท - ไม่มีความแตกต่าง ทั้งสอง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รันไทม์

14

ความแตกต่างคือในตอนแรกเราระบุว่าผู้เรียกต้องส่งผ่านอินสแตนซ์ Object (คลาสใดก็ได้) และจะได้รับ Object อื่นกลับมา (คลาสใด ๆ ไม่จำเป็นต้องเป็นประเภทเดียวกัน)

ในประการที่สองประเภทที่ส่งคืนจะเป็นประเภทเดียวกับที่กำหนดเมื่อคลาสถูกกำหนด

Example ex = new Example<Integer>();

ที่นี่เราระบุประเภท T ที่จะช่วยให้เราบังคับใช้ข้อ จำกัด เพิ่มเติมเกี่ยวกับคลาสหรือวิธีการ ตัวอย่างเช่นเราสามารถสร้างอินสแตนซ์ a LinkedList<Integer>หรือLinkedList<Example>และเรารู้ว่าเมื่อเราเรียกหนึ่งในวิธีการเหล่านี้เราจะได้อินสแตนซ์จำนวนเต็มหรือตัวอย่างกลับคืนมา

เป้าหมายหลักที่นี่คือรหัสการเรียกสามารถระบุประเภทของวัตถุที่คลาสจะดำเนินการแทนการใช้ type-casting เพื่อบังคับใช้สิ่งนี้

ดูJava Generics * จาก Oracle

* อัปเดตลิงค์


13

ความแตกต่างคือด้วยวิธีการทั่วไปฉันไม่จำเป็นต้องแคสต์และฉันได้รับข้อผิดพลาดในการคอมไพล์เมื่อฉันทำผิด:

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;
    }
}

เช่นเดียวกับที่จะบอกว่าคุณไม่จำเป็นต้องโยนวัตถุอีกต่อไปเช่นกันสำหรับ Android 6
John Lord

2

คุณไม่จำเป็นต้องทำการแคสต์คลาสเพิ่มเติม ในกรณีแรกคุณจะได้รับวัตถุของคลาส java.lang.Object เสมอซึ่งคุณจะต้องส่งไปยังคลาสของคุณ ในกรณีที่สอง T จะถูกแทนที่ด้วยคลาสที่กำหนดไว้ในลายเซ็นทั่วไปและไม่จำเป็นต้องมีการแคสต์คลาส


2

ที่รันไทม์ไม่มีอะไรเลย แต่ในเวลาคอมไพล์ครั้งที่สองจะทำการตรวจสอบประเภทเพื่อให้แน่ใจว่าประเภทของพารามิเตอร์และประเภทของค่าที่ส่งคืนตรงกัน (หรือเป็นประเภทย่อยของ) ประเภทใดก็ตามที่ T แก้ไขได้ (ตัวอย่างแรกจะทำการตรวจสอบประเภทด้วย แต่ทุกออบเจ็กต์เป็น a ประเภทย่อยของ Object ดังนั้นทุกประเภทจะได้รับการยอมรับ)


2

T เป็นประเภททั่วไป ความหมายสามารถแทนที่ได้โดยอ็อบเจ็กต์ที่มีคุณสมบัติใด ๆ ในรันไทม์ คุณอาจเรียกใช้วิธีการดังต่อไปนี้:

String response = doSomething("hello world");

หรือ

MyObject response = doSomething(new MyObject());

หรือ

Integer response = doSomething(31);

อย่างที่คุณเห็นมีความหลากหลายอยู่ที่นี่

แต่ถ้ามีการประกาศให้ส่งคืน Object คุณจะไม่สามารถทำได้เว้นแต่คุณจะพิมพ์ cast things


เราสามารถพูดได้ว่า<T>ไม่มีการทำ autoboxing?
SMUsamaShah

0

ในกรณีแรกจะใช้พารามิเตอร์ประเภท egstring และส่งคืนประเภท foo ในกรณีที่สองจะใช้พารามิเตอร์ประเภท foo และส่งคืนอ็อบเจ็กต์ประเภท foo


0

มีเหตุผลบางประการที่คุณสามารถพิจารณา Generics แทนประเภทวัตถุใน Java:

  1. Generics มีความยืดหยุ่นและปลอดภัย ในขณะเดียวกันการทำงานกับ Object ที่ต้องใช้ type-casting มักเกิดข้อผิดพลาด
  2. Type Casting ใน Java นั้นช้า ref: [1]: https://www.infoworld.com/article/2076555/java-performance-programming--part-2--the-cost-of-casting.html
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.