นี่คือโซลูชันที่ได้รับการปรับปรุงตามที่ParameterizedType.getActualTypeArguments
กล่าวไว้แล้วโดย @noah, @Lars Bohl และคนอื่น ๆ
การปรับปรุงเล็กน้อยครั้งแรกในการนำไปใช้ โรงงานไม่ควรส่งคืนอินสแตนซ์ แต่เป็นประเภท ทันทีที่คุณส่งคืนอินสแตนซ์โดยใช้Class.newInstance()
คุณจะลดขอบเขตการใช้งาน เพราะคอนสตรัคเตอร์ที่ไม่มีข้อโต้แย้งเท่านั้นที่สามารถเรียกสิ่งนี้ได้ วิธีที่ดีกว่าคือส่งคืนชนิดและอนุญาตให้ลูกค้าเลือกตัวสร้างที่เขาต้องการเรียกใช้:
public class TypeReference<T> {
public Class<T> type(){
try {
ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
if (pt.getActualTypeArguments() == null || pt.getActualTypeArguments().length == 0){
throw new IllegalStateException("Could not define type");
}
if (pt.getActualTypeArguments().length != 1){
throw new IllegalStateException("More than one type has been found");
}
Type type = pt.getActualTypeArguments()[0];
String typeAsString = type.getTypeName();
return (Class<T>) Class.forName(typeAsString);
} catch (Exception e){
throw new IllegalStateException("Could not identify type", e);
}
}
}
นี่คือตัวอย่างการใช้งาน @Lars Bohl ได้แสดงให้เห็นถึงวิธีการลงชื่อเข้าใช้เพื่อรับการแปลเจนเนอเรชั่นใหม่ผ่านทางส่วนขยาย @noah {}
เพียงผ่านการสร้างอินสแตนซ์ที่มี นี่คือการทดสอบเพื่อแสดงให้เห็นถึงทั้งสองกรณี:
import java.lang.reflect.Constructor;
public class TypeReferenceTest {
private static final String NAME = "Peter";
private static class Person{
final String name;
Person(String name) {
this.name = name;
}
}
@Test
public void erased() {
TypeReference<Person> p = new TypeReference<>();
Assert.assertNotNull(p);
try {
p.type();
Assert.fail();
} catch (Exception e){
Assert.assertEquals("Could not identify type", e.getMessage());
}
}
@Test
public void reified() throws Exception {
TypeReference<Person> p = new TypeReference<Person>(){};
Assert.assertNotNull(p);
Assert.assertEquals(Person.class.getName(), p.type().getName());
Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
Assert.assertNotNull(ctor);
Person person = (Person) ctor.newInstance(NAME);
Assert.assertEquals(NAME, person.name);
}
static class TypeReferencePerson extends TypeReference<Person>{}
@Test
public void reifiedExtenension() throws Exception {
TypeReference<Person> p = new TypeReferencePerson();
Assert.assertNotNull(p);
Assert.assertEquals(Person.class.getName(), p.type().getName());
Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
Assert.assertNotNull(ctor);
Person person = (Person) ctor.newInstance(NAME);
Assert.assertEquals(NAME, person.name);
}
}
หมายเหตุ:คุณสามารถบังคับให้ลูกค้าของTypeReference
มักจะใช้เช่นเมื่อถูกสร้างขึ้นโดยการทำชั้นนี้เป็นนามธรรม:{}
public abstract class TypeReference<T>
ฉันไม่ได้ทำเพียงเพื่อแสดงกรณีทดสอบที่ถูกลบ