Berikut adalah solusi yang ditingkatkan, berdasarkan ParameterizedType.getActualTypeArguments
, sudah disebutkan oleh @noah, @Lars Bohl, dan beberapa lainnya.
Peningkatan kecil pertama dalam implementasi. Pabrik tidak boleh mengembalikan instance, tetapi tipe. Segera setelah Anda kembali contoh menggunakan Class.newInstance()
Anda mengurangi ruang lingkup penggunaan. Karena hanya konstruktor tanpa argumen yang dapat dipanggil seperti ini. Cara yang lebih baik adalah mengembalikan tipe, dan mengizinkan klien untuk memilih, konstruktor mana yang ingin dia panggil:
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);
}
}
}
Berikut adalah contoh penggunaannya. @ Lars Bohl hanya menunjukkan cara signe untuk mendapatkan genenerik terverifikasi melalui ekstensi. @noah hanya melalui membuat instance dengan {}
. Berikut adalah tes untuk menunjukkan kedua kasus:
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);
}
}
Catatan: Anda dapat memaksa klien TypeReference
selalu digunakan {}
ketika misalnya dibuat dengan membuat kelas ini abstrak: public abstract class TypeReference<T>
. Saya belum melakukannya, hanya untuk menunjukkan test case terhapus.