รับชนิดของพารามิเตอร์ทั่วไปใน Java พร้อมการสะท้อน


143

เป็นไปได้ที่จะรับชนิดของพารามิเตอร์ทั่วไปหรือไม่?

ตัวอย่าง:

public final class Voodoo {
    public static void chill(List<?> aListWithTypeSpiderMan) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        Class typeOfTheList = ???;
    }

    public static void main(String... args) {
        chill(new ArrayList<SpiderMan>());
    }
}

คำตอบ:


156

สิ่งก่อสร้างหนึ่งที่ฉันเคยเห็น

Class<T> persistentClass = (Class<T>)
   ((ParameterizedType)getClass().getGenericSuperclass())
      .getActualTypeArguments()[0];

ดูเหมือนจะมีเงาสะท้อนอยู่รอบตัวฉันโชคไม่ดีที่ไม่เข้าใจ ... ขอโทษ


2
แต่ประกาศว่าเป็นนามธรรมแล้ว เมื่อคุณต้องการที่จะใช้มันแล้ว sublclass มันแบบอินไลน์
Snicolas

42
แม้ว่านี่จะเก่ามากและยอมรับด้วยเหตุผลบางอย่างฉันก็ลงคะแนนเพราะมันไม่ตอบคำถาม มันจะไม่ให้ "SpiderMan" ในตัวอย่างที่ให้ไว้ในคำถาม มันมีประโยชน์อย่างแน่นอนในบางสถานการณ์ แต่ใช้ไม่ได้กับคำถามที่ถาม
Jon Skeet

15
ไม่มีใครที่มาไกลขนาดนี้กำลังจะยอมรับคำตอบ "ไม่สามารถทำได้" เรามาที่นี่เพราะเราหมดหวัง
Addison

14
ฉันได้รับข้อยกเว้นนี้: Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType ไม่แน่ใจว่าข้อ จำกัด คืออะไร
จาน

4
เช่นเดียวกับ @Dish ฉันเพิ่งได้รับjava.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
Kenny Wyland

133

ฉันต้องการลองคำตอบจาก @DerMike เพื่ออธิบาย:

ก่อนอื่นให้ลบประเภทไม่ได้หมายความว่า JDK กำจัดข้อมูลประเภทที่รันไทม์ เป็นวิธีการอนุญาตให้มีการตรวจสอบประเภทเวลาคอมไพล์และความเข้ากันได้ของชนิดรันไทม์เพื่ออยู่ร่วมกันในภาษาเดียวกัน เมื่อบล็อกของรหัสนี้แสดงถึง JDK จะเก็บข้อมูลประเภทที่ถูกลบ - มันไม่ได้เกี่ยวข้องกับการคัดสรรและสิ่งต่างๆ

ประการที่สองนี้ให้ข้อมูลประเภททั่วไปกับคลาสทั่วไปหนึ่งระดับขึ้นไปของการสืบทอดจากประเภทคอนกรีตที่กำลังตรวจสอบ - นั่นคือคลาสนามธรรมหลักที่มีพารามิเตอร์ประเภททั่วไปสามารถค้นหาประเภทคอนกรีตที่สอดคล้องกับพารามิเตอร์ประเภทสำหรับการใช้งานที่เป็นรูปธรรมของตัวเอง ที่สืบทอดโดยตรงจากมัน ถ้าคลาสนี้ไม่ใช่แบบนามธรรมและอินสแตนซ์หรือการใช้งานที่เป็นรูปธรรมลดลงสองระดับสิ่งนี้จะไม่ทำงาน (ถึงแม้จะมีเล่ห์เหลี่ยมนิดหน่อยก็สามารถทำให้มันใช้ได้กับจำนวนที่กำหนดไว้ล่วงหน้าเกินกว่าหนึ่งระดับหรือต่ำกว่าระดับต่ำสุด ด้วยพารามิเตอร์ประเภท X ทั่วไปและอื่น ๆ )

อย่างไรก็ตามเพื่ออธิบาย นี่คือรหัสอีกครั้งโดยแยกออกเป็นบรรทัดเพื่อความสะดวกในการอ้างอิง:

1 # class genericParameter0OfThisClass = 
2 # (คลาส)
3 # ((ประเภทพารามิเตอร์)
4 # getClass ()
5 # .getGenericSuperclass ())
6 # .getActualTypeArguments () [0];

ให้ 'เรา' เป็นคลาสนามธรรมที่มีประเภททั่วไปที่มีรหัสนี้ การอ่านข้างในนี้โดยสังเขป:

  • บรรทัดที่ 4 ได้รับอินสแตนซ์ของคลาสคลาสคอนกรีตปัจจุบัน สิ่งนี้ระบุประเภทที่เป็นรูปธรรมของผู้สืบทอดในทันที
  • บรรทัดที่ 5 ได้รับ supertype ของคลาสนั้นเป็นประเภท นี่คือเรา. เนื่องจากเราเป็นประเภทพารามิเตอร์เราจึงสามารถโยนตัวเองไปที่ ParameterizedType (บรรทัดที่ 3) ได้อย่างปลอดภัย ที่สำคัญคือเมื่อ Java กำหนดวัตถุประเภทนี้จะใช้ข้อมูลประเภทที่มีอยู่ในเด็กที่จะเชื่อมโยงข้อมูลประเภทกับพารามิเตอร์ประเภทของเราในอินสแตนซ์ ParameterizedType ใหม่ ดังนั้นตอนนี้เราสามารถเข้าถึงประเภทคอนกรีตสำหรับข้อมูลทั่วไปของเรา
  • Line 6 ได้รับอาเรย์ของประเภทที่แมปไว้ในข้อมูลทั่วไปของเราตามลำดับที่ประกาศไว้ในรหัสชั้นเรียน สำหรับตัวอย่างนี้เราดึงพารามิเตอร์แรกออกมา สิ่งนี้กลับมาเป็นประเภท
  • บรรทัด 2 ปลดเปลื้อง Type สุดท้ายที่ส่งคืนไปยัง Class สิ่งนี้ปลอดภัยเพราะเรารู้ว่าพารามิเตอร์ประเภททั่วไปประเภทใดที่สามารถใช้และสามารถยืนยันได้ว่าพวกเขาทั้งหมดจะเป็นคลาส (ฉันไม่แน่ใจว่าใน Java หนึ่งจะไปเกี่ยวกับการรับพารามิเตอร์ทั่วไปที่ไม่มีอินสแตนซ์ Class เกี่ยวข้องกับมันจริง ๆ )

... และมันก็สวยมาก ดังนั้นเราจึงผลักดันข้อมูลประเภทจากการนำไปใช้ที่เป็นรูปธรรมของเราเองและใช้เพื่อเข้าถึงตัวจัดการคลาส เราสามารถเพิ่ม getGenericSuperclass () และเพิ่มสองระดับหรือกำจัด getGenericSuperclass () และรับค่าสำหรับตัวเราในรูปแบบคอนกรีต (ข้อแม้: ฉันยังไม่ได้ทดสอบสถานการณ์เหล่านี้พวกเขายังไม่ได้มาสำหรับฉัน)

มันจะยุ่งยากหากว่าเด็กที่เป็นรูปธรรมของคุณมีจำนวนฮ็อพออกไปโดยพลการหรือหากคุณเป็นรูปธรรมและไม่เป็นที่สิ้นสุดและโดยเฉพาะอย่างยิ่งหากคุณคาดหวังว่าเด็ก ๆ แต่โดยปกติคุณสามารถออกแบบรอบการพิจารณาเหล่านั้นเพื่อให้ได้รับมากที่สุด

หวังว่านี่จะช่วยใครซักคน! ฉันรู้จักโพสต์นี้โบราณ ฉันอาจจะตัดคำอธิบายนี้และเก็บไว้สำหรับคำถามอื่น ๆ


3
เพียงแค่ตอบของคุณ "ฉันไม่แน่ใจว่าใน Java หนึ่งจะไปเกี่ยวกับการรับพารามิเตอร์ทั่วไปที่ไม่มีอินสแตนซ์ Class ที่เกี่ยวข้องกับมันจริง ๆ "): เป็นไปได้ถ้าพารามิเตอร์ของคลาสทั่วไปเป็นทั้ง " นิพจน์ตัวแทน "(พูด:? ขยาย SomeClass) หรือ" ตัวแปรประเภท "(พูด: <T> โดยที่ T มาจากคลาสย่อยทั่วไป) ในทั้งสองกรณีอินสแตนซ์ "Type" ที่ส่งคืนไม่สามารถส่งไปยัง "Class" เนื่องจากแต่ละ VM อาจมีการใช้งานที่แตกต่างกันสำหรับทั้งสองที่ใช้อินเตอร์เฟสประเภท แต่ไม่ได้รับโดยตรงจาก java.lang.Class
Roberto Andrade

19

ที่จริงฉันได้งานนี้ พิจารณาตัวอย่างต่อไปนี้:

Method m;
Type[] genericParameterTypes = m.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
     if( genericParameterTypes[i] instanceof ParameterizedType ) {
                Type[] parameters = ((ParameterizedType)genericParameterTypes[i]).getActualTypeArguments();
//parameters[0] contains java.lang.String for method like "method(List<String> value)"

     }
 }

ฉันใช้ jdk 1.6


12
-1: นี่ไม่ได้แก้ปัญหาในคำถาม; มันให้ชนิดที่ประกาศในลายเซ็นเมธอดไม่ใช่ประเภทรันไทม์จริง
Michael Borgwardt

5
อาจไม่ตอบคำถามของ OP แต่เป็นเทคนิคที่มีประโยชน์
dnault

3
นี่คือคำตอบของคำถามที่แตกต่างอย่างสิ้นเชิง
Dawood ibn Kareem

คุณสามารถโทร m.getParameterTypes () เพื่อรับชนิดจริงทั้งหมด มันจะส่งคืน "รายการ" สำหรับตัวอย่าง
Lee Meador

หาก genericParameterTypes [i] ไม่ใช่อินสแตนซ์ของ ParameterizedType ก็อาจเป็นคลาสได้ นั่นหมายความว่าอาร์กิวเมนต์เฉพาะของวิธีการนั้นไม่ได้รับการแปรค่าพารามิเตอร์เลย
Lee Meador

18

มีวิธีการแก้ปัญหาจริงโดยใช้เคล็ดลับ "ระดับที่ไม่ระบุชื่อ"และความคิดจากSuper Type Tokens :

public final class Voodoo {
    public static void chill(final List<?> aListWithSomeType) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        System.out.println(aListWithSomeType.getClass().getGenericSuperclass());
        System.out.println(((ParameterizedType) aListWithSomeType
            .getClass()
            .getGenericSuperclass()).getActualTypeArguments()[0]);
    }
    public static void main(String... args) {
        chill(new ArrayList<SpiderMan>() {});
    }
}
class SpiderMan {
}

เคล็ดลับการโกหกในการสร้างนั้นระดับที่ไม่ระบุชื่อ , new ArrayList<SpiderMan>() {}ในสถานที่ของเดิม new ArrayList<SpiderMan>()(แบบง่าย) การใช้งานของชั้น anoymous (ถ้าเป็นไปได้) เพื่อให้แน่ใจว่าคอมไพเลอร์ยังคงมีข้อมูลเกี่ยวกับการโต้แย้งชนิดที่กำหนดให้พารามิเตอร์ชนิดSpiderMan List<?>วันนี้!


คำตอบนี้ทางอ้อมคำถาม: ปัญหาดั้งเดิมไม่มีวิธีแก้ปัญหา เพื่อให้ประเภทเก็บพารามิเตอร์ทั่วไปจะต้องมีการฝังในประเภทอื่นเช่นคลาสย่อย ตัวอย่างนี้แสดงวิธีที่คุณต้องการเปลี่ยนรหัสการโทรเพื่อให้สามารถกู้คืนพารามิเตอร์ชนิดทั่วไปใน chill ()
Florian F

FYI, ลิงค์แรกตาย
Ashvin Sharma

@AshvinSharma ฉันเชื่อว่าวัสดุเดียวกันมีอยู่ที่นี่: rgomes.info/using-typetokens-to-retrieve-generic-parameters
Yann-GaëlGuéhéneuc

7

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

public class Main {

    public static void main(String[] args) {
        doStuff(new LinkedList<String>(), String.class);

    }

    public static <E> void doStuff(List<E> list, Class<E> clazz) {

    }

}

7

คำตอบของภาคผนวก @ @ DerMike สำหรับการรับพารามิเตอร์ทั่วไปของอินเตอร์เฟซที่ทำให้เป็นพารามิเตอร์ (โดยใช้วิธี#getGenericInterfaces ()ภายในวิธีการเริ่มต้น Java-8 เพื่อหลีกเลี่ยงการทำซ้ำ):

import java.lang.reflect.ParameterizedType; 

public class ParametrizedStuff {

@SuppressWarnings("unchecked")
interface Awesomable<T> {
    default Class<T> parameterizedType() {
        return (Class<T>) ((ParameterizedType)
        this.getClass().getGenericInterfaces()[0])
            .getActualTypeArguments()[0];
    }
}

static class Beer {};
static class EstrellaGalicia implements Awesomable<Beer> {};

public static void main(String[] args) {
    System.out.println("Type is: " + new EstrellaGalicia().parameterizedType());
    // --> Type is: ParameterizedStuff$Beer
}

นี่ไม่ใช่สิ่งที่ขอต้นฉบับ Awesomeable<Beer>ที่นี่คุณต้องสร้างคลาสย่อยของ ในกรณีนั้นข้อมูลประเภทจะถูกเก็บรักษาไว้ หากคุณผ่านnew Awesomable<Beer> ()ไปยังวิธีที่จะไม่ทำงาน
Florian F

@ FlorianF หากคุณผ่านไปได้Awesomable<Beer>ทันทีโดยไม่มีคำจำกัดความที่ชัดเจนของคลาสย่อยคอนกรีตEstrellaGaliciaในกรณีนี้ก็ยังคงได้รับประเภทพารามิเตอร์: ฉันวิ่งมันตอนนี้: System.out.println("Type is: " + new Awesomable<Beer>() {}.parameterizedType());---> ประเภทคือ: ParameterizedStuff $ Beer
Campa

6

ไม่นั่นเป็นไปไม่ได้ เนื่องจากปัญหาความเข้ากันได้ที่ต่ำลงข้อมูลทั่วไปของ Java จะขึ้นอยู่กับการลบประเภท ia ตอนรันไทม์สิ่งที่คุณมีคือListวัตถุที่ไม่ใช่แบบทั่วไป มีข้อมูลบางอย่างเกี่ยวกับพารามิเตอร์ประเภทที่รันไทม์ แต่มันอยู่ในคำจำกัดความของคลาส (เช่นคุณสามารถถาม " คำจำกัดความของฟิลด์นี้ใช้ประเภทใด? ") ไม่ใช่ในอินสแตนซ์ของวัตถุ


แม้ว่าจาวาสามารถเก็บข้อมูลนี้ไว้เป็นข้อมูลเมตาระหว่างรันไทม์ได้หรือไม่ ฉันหวังว่ามันจะ โชคร้าย.
cimnine

1
@ user99475: ไม่ ฉันถูกและ Andrey ผิด เขาอ้างถึงข้อมูลประเภทที่ฉันพูดถึงในส่วนที่สองของคำตอบของฉัน แต่นั่นไม่ใช่คำถามที่ถาม
Michael Borgwardt

5

ดังที่กล่าวโดย @bertolami เป็นไปไม่ได้ที่เราจะพิมพ์ตัวแปรและรับค่าในอนาคต (เนื้อหาของตัวแปร typeOfList)

อย่างไรก็ตามคุณสามารถส่งผ่านคลาสเป็นพารามิเตอร์ในรูปแบบนี้:

public final class voodoo {
    public static void chill(List<T> aListWithTypeSpiderMan, Class<T> clazz) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        Class typeOfTheList = clazz;
    }

    public static void main(String... args) {
        chill(new List<SpiderMan>(), Spiderman.class );
    }
}

นั่นคือมากกว่าหรือน้อยกว่าสิ่งที่ Google ไม่เมื่อคุณมีการส่งผ่านตัวแปรระดับการคอนสตรัคของActivityInstrumentationTestCase2


3

ไม่เป็นไปไม่ได้

คุณสามารถได้รับประเภททั่วไปของเขตข้อมูลที่กำหนดให้ชั้นเรียนเป็นข้อยกเว้นเพียงอย่างเดียวกับกฎนั้นและแม้กระทั่งว่าเป็นบิตของการแฮ็ก

ดูที่การรู้ประเภททั่วไปใน Javaสำหรับตัวอย่างนั้น


1

คุณสามารถรับชนิดของพารามิเตอร์ทั่วไปที่มีการสะท้อนเช่นในตัวอย่างนี้ที่ฉันพบที่นี่ :

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class Home<E> {
    @SuppressWarnings ("unchecked")
    public Class<E> getTypeParameterClass(){
        Type type = getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        return (Class<E>) paramType.getActualTypeArguments()[0];
    }

    private static class StringHome extends Home<String>{}
    private static class StringBuilderHome extends Home<StringBuilder>{}
    private static class StringBufferHome extends Home<StringBuffer>{}   

    /**
     * This prints "String", "StringBuilder" and "StringBuffer"
     */
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Object object0 = new StringHome().getTypeParameterClass().newInstance();
        Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
        Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
        System.out.println(object0.getClass().getSimpleName());
        System.out.println(object1.getClass().getSimpleName());
        System.out.println(object2.getClass().getSimpleName());
    }
}

คุณจะทำสิ่งเดียวกันเพื่อรับ V of Home <K, V> ได้อย่างไร
Rafael Sanches

1

คำตอบที่รวดเร็วของคำถามคือคุณไม่สามารถทำได้เนื่องจากการลบประเภท Java ทั่วไป

คำตอบที่ยาวกว่าคือถ้าคุณสร้างรายการของคุณเช่นนี้:

new ArrayList<SpideMan>(){}

จากนั้นในกรณีนี้ประเภททั่วไปจะถูกเก็บไว้ในซูเปอร์คลาสทั่วไปของคลาสนิรนามใหม่ข้างต้น

ไม่ใช่ว่าฉันแนะนำให้ทำเช่นนี้กับรายการ แต่เป็นการใช้งานฟัง:

new Listener<Type>() { public void doSomething(Type t){...}}

และเนื่องจากการคาดการณ์ประเภทซูเปอร์คลาสทั่วไปและซูเปอร์อินเทอร์เฟซเปลี่ยนไประหว่าง JVMs โซลูชันทั่วไปจึงไม่ได้ตรงไปข้างหน้าเพราะคำตอบบางอย่างอาจแนะนำ

ตอนนี้ฉันทำแล้ว


0

สิ่งนี้เป็นไปไม่ได้เพราะ generics ใน Java ได้รับการพิจารณาในเวลารวบรวมเท่านั้น ดังนั้น generics Java เป็นเพียง pre-processor บางชนิด อย่างไรก็ตามคุณสามารถรับคลาสจริงของสมาชิกของรายการ


ใช่นั่นคือสิ่งที่ฉันกำลังทำอยู่ตอนนี้ แต่ตอนนี้ฉันต้องการทราบประเภทแม้ว่ารายการจะว่างเปล่า แต่ผู้ชายสี่คนคงไม่ผิดหรอก ขอบคุณทุกคน)!
cimnine

19
นี่ไม่เป็นความจริง. ในขณะที่มันยุ่งยากคุณสามารถเห็นในโพสต์ถัดไปที่ ParameterizedType อนุญาตให้ทำ
Edmondo1984

1
เห็นด้วยก็สามารถทำได้ ตัวอย่างจาก DerMike สรุปไว้ (ใช้ได้สำหรับฉัน)
แม็

LOL ที่ "สี่คนไม่ผิด" คำตอบนี้ถูกต้อง
Dawood ibn Kareem

0

Iterable<?...>ฉันได้รหัสนี้สำหรับวิธีการซึ่งคาดว่าจะยอมรับหรือผลตอบแทน นี่คือรหัส:

/**
 * Assuming the given method returns or takes an Iterable<T>, this determines the type T.
 * T may or may not extend WindupVertexFrame.
 */
private static Class typeOfIterable(Method method, boolean setter)
{
    Type type;
    if (setter) {
        Type[] types = method.getGenericParameterTypes();
        // The first parameter to the method expected to be Iterable<...> .
        if (types.length == 0)
            throw new IllegalArgumentException("Given method has 0 params: " + method);
        type = types[0];
    }
    else {
        type = method.getGenericReturnType();
    }

    // Now get the parametrized type of the generic.
    if (!(type instanceof ParameterizedType))
        throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);
    ParameterizedType pType = (ParameterizedType) type;
    final Type[] actualArgs = pType.getActualTypeArguments();
    if (actualArgs.length == 0)
        throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);

    Type t = actualArgs[0];
    if (t instanceof Class)
        return (Class<?>) t;

    if (t instanceof TypeVariable){
        TypeVariable tv =  (TypeVariable) actualArgs[0];
        AnnotatedType[] annotatedBounds = tv.getAnnotatedBounds();///
        GenericDeclaration genericDeclaration = tv.getGenericDeclaration();///
        return (Class) tv.getAnnotatedBounds()[0].getType();
    }

    throw new IllegalArgumentException("Unknown kind of type: " + t.getTypeName());
}

0

คุณไม่สามารถรับพารามิเตอร์ทั่วไปจากตัวแปรได้ แต่คุณสามารถทำได้จากเมธอดหรือการประกาศฟิลด์:

Method method = getClass().getDeclaredMethod("chill", List.class);
Type[] params = method.getGenericParameterTypes();
ParameterizedType firstParam = (ParameterizedType) params[0];
Type[] paramsOfFirstGeneric = firstParam.getActualTypeArguments();

สิ่งนี้ทำให้ฉันมีข้อยกเว้นในเธรด "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl
Lluis Martinez

'params' คือ 'Type' ซึ่งมีหลายอินเตอร์เฟสย่อย TypeVariableImpl เป็นหนึ่งที่ใช้สำหรับการขัดแย้งวิธีและมันบอกชื่อของทั่วไปใน <> s ไม่ได้เป็นคลาสที่มันหมายถึง
Lee Meador

0

เพียงฉันอ่านโค้ดนี้ยากฉันก็แบ่งมันเป็น 2 บรรทัดอ่านได้:

// assuming that the Generic Type parameter is of type "T"
ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> c =(Class<T>)p.getActualTypeArguments()[0];

ฉันต้องการสร้างตัวอย่างของพารามิเตอร์ประเภทโดยไม่ต้องมีพารามิเตอร์ใด ๆ กับวิธีการของฉัน:

publc T getNewTypeInstance(){
    ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
    Class<T> c =(Class<T>)p.getActualTypeArguments()[0];

    // for me i wanted to get the type to create an instance
    // from the no-args default constructor
    T t = null;
    try{
        t = c.newInstance();
    }catch(Exception e){
        // no default constructor available
    }
    return t;
}

0

นี่คือเคล็ดลับอื่น ใช้อาร์เรย์ vararg ทั่วไป

import java.util.ArrayList;

class TypedArrayList<E> extends ArrayList<E>
{
    @SafeVarargs
    public TypedArrayList (E... typeInfo)
    {
        // Get generic type at runtime ...
        System.out.println (typeInfo.getClass().getComponentType().getTypeName());
    }
}

public class GenericTest
{
    public static void main (String[] args)
    {
        // No need to supply the dummy argument
        ArrayList<Integer> ar1 = new TypedArrayList<> ();
        ArrayList<String> ar2 = new TypedArrayList<> ();
        ArrayList<?> ar3 = new TypedArrayList<> ();
    }
}

0

ฉันสังเกตเห็นว่าผู้คนมากมายโน้มตัวไปหาคำgetGenericSuperclass()ตอบ:

class RootGeneric<T> {
  public Class<T> persistentClass = (Class<T>)
    ((ParameterizedType)getClass().getGenericSuperclass())
      .getActualTypeArguments()[0];
}

อย่างไรก็ตามวิธีนี้มีข้อผิดพลาดเกิดขึ้นได้ง่าย มันจะไม่ทำงานอย่างถูกต้องหากมียาชื่อสามัญในลูกหลาน พิจารณาสิ่งนี้:

class Foo<S> extends RootGeneric<Integer> {}

class Bar extends Foo<Double> {}

ประเภทไหนจะBar.persistentClassมี? Class<Integer>? Nope Class<Double>มันจะเป็น นี้จะเกิดขึ้นเนื่องจากการgetClass()เสมอกลับด้านบนระดับมากที่สุดซึ่งเป็นในกรณีนี้และชั้นซุปเปอร์ของทั่วไปคือBar Foo<Double>ดังนั้นประเภทอาร์กิวเมนต์จะเป็นDoubleดังนั้นประเภทอาร์กิวเมนต์จะ

หากคุณต้องการโซลูชันที่เชื่อถือได้ซึ่งไม่ล้มเหลวฉันสามารถแนะนำสองวิธี

  1. Guavaใช้ มีคลาสที่สร้างขึ้นเพื่อจุดประสงค์นี้อย่างแท้จริง: com.google.common.reflect.TypeToken. มันจัดการกรณีมุมทั้งหมดได้ดีและมีฟังก์ชั่นที่ดีขึ้น ข้อเสียคือการพึ่งพาพิเศษ เมื่อคุณใช้คลาสนี้รหัสของคุณจะดูเรียบง่ายและชัดเจนดังนี้:
class RootGeneric<T> {
  @SuppressWarnings("unchecked")
  public final Class<T> persistentClass = (Class<T>) (new TypeToken<T>(getClass()) {}.getType());
}
  1. ใช้วิธีการที่กำหนดเองด้านล่าง มันใช้ตรรกะที่ง่ายขึ้นอย่างมีนัยสำคัญคล้ายกับคลาสของ Guava ที่กล่าวถึงข้างต้น อย่างไรก็ตามฉันไม่รับประกันว่าจะเกิดข้อผิดพลาดได้ง่าย มันจะแก้ปัญหากับลูกหลานทั่วไปว่า
abstract class RootGeneric<T> {
  @SuppressWarnings("unchecked")
  private Class<T> getTypeOfT() {
    Class<T> type = null;
    Class<?> iter = getClass();
    while (iter.getSuperclass() != null) {
      Class<?> next = iter.getSuperclass();
      if (next != null && next.isAssignableFrom(RootGeneric.class)) {
        type =
            (Class<T>)
                ((ParameterizedType) iter.getGenericSuperclass()).getActualTypeArguments()[0];
        break;
      }
      iter = next;
    }
    if (type == null) {
      throw new ClassCastException("Cannot determine type of T");
    }
    return type;
  }
}

-1

ใช้:

Class<?> typeOfTheList = aListWithTypeSpiderMan.toArray().getClass().getComponentType();

นี่เป็นสิ่งที่ผิด ผลตอบแทนtoArray() Object[]คุณไม่สามารถและไม่ควรขึ้นอยู่กับว่ามันเป็นประเภทย่อยบางอย่าง
Radiodef
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.