วิธีใช้ Class <T> ใน Java


247

มีการอภิปรายที่ดีเกี่ยวกับ Generics และสิ่งที่พวกเขาทำอยู่เบื้องหลังในคำถามนี้ดังนั้นเราทุกคนรู้ว่านั่นVector<int[]>คือเวกเตอร์ของอาร์เรย์จำนวนเต็มและHashTable<String, Person>เป็นตารางที่คีย์เป็นสตริงและค่าPersons Class<>แต่สิ่งที่ตอฉันก็คือการใช้งานของ

คลาส java Classควรใช้ชื่อเทมเพลตด้วย (หรือดังนั้นฉันถูกบอกโดยขีดเส้นใต้สีเหลืองใน eclipse) ฉันไม่เข้าใจสิ่งที่ฉันควรใส่ จุดรวมของClassวัตถุคือเมื่อคุณไม่มีข้อมูลเกี่ยวกับวัตถุอย่างสมบูรณ์เพื่อสะท้อนและเช่นนั้น ทำไมมันทำให้ฉันระบุคลาสที่Classวัตถุจะเก็บ? ฉันไม่รู้แน่ชัดหรือไม่ใช้Classวัตถุฉันจะใช้วัตถุเฉพาะ

คำตอบ:


136

การใช้คลาสรุ่นที่ได้รับการแก้ไขช่วยให้คุณสามารถเขียนสิ่งต่าง ๆ เช่น

Class<? extends Collection> someCollectionClass = someMethod();

และจากนั้นคุณสามารถมั่นใจได้ว่าวัตถุคลาสที่คุณได้รับการขยายCollectionและตัวอย่างของคลาสนี้จะเป็น (อย่างน้อย) คอลเลกชัน


183

สิ่งที่เรารู้คือ " อินสแตนซ์ทั้งหมดของคลาสใด ๆ จะใช้ออบเจ็กต์ 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 ด้วย

สัญลักษณ์แทนและการพิมพ์ย่อย

อาร์กิวเมนต์ประเภทสำหรับวิธีการทั่วไป


2
คำตอบของฉันเกี่ยวกับสัญลักษณ์แทน bounded stackoverflow.com/questions/1368166/...
Kanagavelu Sugumar

"Class <T> หมายถึงคลาสอ็อบเจ็กต์ที่มีประเภทคลาส 'T' ที่เฉพาะเจาะจงซึ่งไม่มีเหตุผล ขอบคุณ ..
bynu022

คำตอบนี้สร้างความสับสนอย่างมากในการใช้ Classes (จากโรงเรียน) ในคำถามเกี่ยวกับ Classes (ใน Java) เป็นการยากที่จะทราบว่าผู้เขียนกำลังพูดถึงอะไรจากประโยคหนึ่งไปอีกประโยคหนึ่ง
Echox

@Echox ฉันขอโทษฉันสามารถปรับปรุงได้หากคุณมีคำถามใด ๆ
Kanagavelu Sugumar


32

จากเอกสาร Java:

[... ] น่าแปลกที่คลาสคลาสนั้นได้รับการสร้างขึ้น คลาสตัวอักษรตอนนี้ทำหน้าที่เป็นโทเค็นประเภทให้ข้อมูลทั้งชนิดเวลาทำงานและเวลารวบรวม สิ่งนี้เปิดใช้งานรูปแบบของโรงงานแบบคงที่สุดขั้วโดยวิธี getAnnotation ในอินเทอร์เฟซ AnnotatedElement ใหม่:

<T extends Annotation> T getAnnotation(Class<T> annotationType); 

นี่เป็นวิธีการทั่วไป มันส่งผ่านค่าของพารามิเตอร์ประเภท T จากอาร์กิวเมนต์และส่งคืนอินสแตนซ์ที่เหมาะสมของ T ดังที่แสดงโดยตัวอย่างต่อไปนี้:

Author a = Othello.class.getAnnotation(Author.class);

ก่อนที่จะมีชื่อสามัญคุณจะต้องส่งผลให้ผู้แต่ง นอกจากนี้คุณจะไม่มีทางทำการตรวจสอบคอมไพเลอร์ว่าพารามิเตอร์จริงแสดงคลาสย่อยของคำอธิบายประกอบ [ ... ]

ฉันไม่เคยใช้สิ่งนี้เลย ใคร?


2
ฉัน (ฉันคิดว่า) ทำ เฟรมเวิร์ก (แปลก ๆ ) ฉันทำงานร่วมกับคุณกำหนดให้ส่งชื่อ class of services services ที่โมดูลของคุณพึ่งพา ฉันสร้างเลเยอร์ด้านบนของที่ใช้วัตถุคลาสเพื่อ จำกัด จำนวนตัวเลือก เมื่อใช้Class<? extends X>สัญกรณ์ที่ฉันพบฉันสามารถ จำกัด เฉพาะประเภท 'บริการ' เท่านั้น ยกเว้นไม่มีทั่วไป 'บริการ' Class<?>ชนิดดังนั้นฉันสามารถทำมันได้ด้วย อนิจจา.
fwielstra

9

ฉันพบว่าclass<T>มีประโยชน์เมื่อฉันสร้างการค้นหารีจีสทรีบริการ เช่น

<T> T getService(Class<T> serviceClass)
{
    ...
}

5

ตามที่คำตอบอื่น ๆ ชี้ให้เห็นมีเหตุผลมากมายและเหตุผลที่ดีที่ทำให้สิ่งนี้classเป็นเรื่องทั่วไป Class<T>แต่มีความอุดมสมบูรณ์ของเวลาที่คุณไม่ได้มีวิธีการรู้ประเภททั่วไปที่จะใช้กับใด ๆ ในกรณีเหล่านี้คุณสามารถละเว้นคำเตือน eclipse สีเหลืองหรือคุณสามารถใช้Class<?>... นั่นคือสิ่งที่ฉันทำ;)


@SuppressWarnings("unchecked")มาเพื่อช่วยเหลือ! (เพียงระมัดระวังเสมอนำมันไปใช้เป็นขอบเขตที่มีขนาดเล็กที่สุดเท่าที่มันจะเกิดปัญหาที่อาจเกิดขึ้นชัดเจนในรหัสของคุณ.)
Donal Fellows

4

ต่อไปนี้ตามคำตอบของ @Kire Haglin ตัวอย่างเพิ่มเติมของวิธีการทั่วไปสามารถดูได้ในเอกสารประกอบสำหรับการยกเลิกการแจ้ง JAXB :

public <T> T unmarshal( Class<T> docClass, InputStream inputStream )
         throws JAXBException {
  String packageName = docClass.getPackage().getName();
  JAXBContext jc = JAXBContext.newInstance( packageName );
  Unmarshaller u = jc.createUnmarshaller();
  JAXBElement<T> doc = (JAXBElement<T>)u.unmarshal( inputStream );
  return doc.getValue();
}

สิ่งนี้อนุญาตให้unmarshalส่งคืนเอกสารประเภทต้นไม้เนื้อหา JAXB โดยพลการ


2

Classคุณมักจะต้องการที่จะใช้สัญลักษณ์แทนด้วย ยกตัวอย่างเช่นClass<? extends JComponent>จะอนุญาตให้คุณระบุว่าชั้นเป็น subclass JComponentของบางส่วน หากคุณได้รับClassอินสแตนซ์จากClass.forNameนั้นคุณสามารถใช้Class.asSubclassเพื่อดำเนินการส่งก่อนที่จะพยายามพูดพูดสร้างอินสแตนซ์


2

ใน java <T>หมายถึงคลาสทั่วไป Generic Class เป็นคลาสที่สามารถทำงานกับชนิดข้อมูลใด ๆหรือในคำอื่น ๆ ที่เราสามารถพูดได้ว่าเป็นชนิดข้อมูลที่เป็นอิสระ

public class Shape<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

โดยที่Tหมายถึงพิมพ์ ตอนนี้เมื่อคุณสร้างอินสแตนซ์ของคลาส Shape นี้คุณจะต้องแจ้งให้คอมไพเลอร์ทราบว่าจะใช้ชนิดข้อมูลใด

ตัวอย่าง:

Shape<Integer> s1 = new Shape();
Shape<String> s2 = new Shape();

จำนวนเต็มเป็นชนิดและสตริงเป็นชนิดด้วย

<T>เฉพาะหมายถึงประเภททั่วไป อ้างอิงจากเอกสาร Java - ประเภททั่วไปเป็นชั้นเรียนทั่วไปหรืออินเตอร์เฟซที่แปรตามประเภท


0

เพียงเพื่อแสดงตัวอย่างอื่นรุ่นทั่วไปของ Class ( Class<T>) อนุญาตให้หนึ่งเขียนฟังก์ชันทั่วไปเช่นหนึ่งด้านล่าง

public static <T extends Enum<T>>Optional<T> optionalFromString(
        @NotNull Class<T> clazz,
        String name
) {
    return Optional<T> opt = Optional.ofNullable(name)
            .map(String::trim)
            .filter(StringUtils::isNotBlank)
            .map(String::toUpperCase)
            .flatMap(n -> {
                try {
                    return Optional.of(Enum.valueOf(clazz, n));
                } catch (Exception e) {
                    return Optional.empty();
                }
            });
}

-2

มันเริ่มสับสนในตอนแรก แต่ช่วยในสถานการณ์ด้านล่าง:

class SomeAction implements Action {
}

// Later in the code.
Class<Action> actionClass = Class.forName("SomeAction"); 
Action action = actionClass.newInstance();
// Notice you get an Action instance, there was no need to cast.

4
นั่นเป็นเพียงวิธีที่ซับซ้อนอย่างไม่น่าเชื่อในการพูดคำสั่ง Action a = new Action () ใช่ไหม
Karl

1
Action a = new Action() ? Actionฉันเป็นอินเทอร์เฟซSomeActionเรากำลังพยายามหาอินสแตนซ์ของ เรามีชื่อของSomeActionพร้อมใช้งานที่รันไทม์เท่านั้น
fastcodejava

สิ่งนี้ไม่ผ่านการตรวจสอบชนิด: คอมไพเลอร์ java ไม่มีวิธีที่จะบอกได้ว่า <code> Class.forName ("SomeAction") </code> จะเป็นประเภท <code>Class<Action> </code> เนื่องจากสิ่งนี้จะเป็นเพียง เป็นที่รู้จักในขณะทำงาน
tonio

@tonio ใช่มั้ยดังนั้นคุณอาจจะต้องตัดบรรทัดแรกนั้นด้วยการลอง / จับ แต่สมมติว่าไม่มีข้อยกเว้นถูกโยนลงไปที่นั่นบรรทัดที่สองรับประกันว่าจะทำงาน
MatrixFrog

2
สิ่งที่คุณกำลังอธิบายจริงๆคือ SomeAction.class ตรงกับรูปแบบ Class <? ขยายการดำเนินการ> - นั่นคือถ้าคุณมีวิธีใช้ useAction (Class <? extends Action> klass) คุณสามารถเรียกใช้ useAction (SomeAction.class)
Thomas Andrews

-5

เพียงใช้คลาสเนื้อ:

public <T> T beefmarshal( Class<beef> beefClass, InputBeef inputBeef )
     throws JAXBException {
     String packageName = docClass.getPackage().getBeef();
     JAXBContext beef = JAXBContext.newInstance( packageName );
     Unmarshaller u = beef.createBeef();
     JAXBElement<T> doc = (JAXBElement<T>)u.beefmarshal( inputBeef );
     return doc.getBeef();
}

4
สิ่งนี้ไม่ได้ให้คำตอบสำหรับคำถาม หากคุณคิดว่าคุณมีสิ่งที่จะเพิ่มแก้ไขคำตอบนี้
MasterAM
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.