Đây là một giải pháp cải tiến, dựa trên ParameterizedType.getActualTypeArguments
, đã được đề cập bởi @noah, @Lars Bohl và một số người khác.
Cải tiến nhỏ đầu tiên trong việc thực hiện. Nhà máy không nên trả lại ví dụ, nhưng một loại. Ngay khi bạn trả lại cá thể bằng cách sử dụng, Class.newInstance()
bạn sẽ giảm phạm vi sử dụng. Bởi vì chỉ có các hàm tạo không có đối số mới có thể được gọi như thế này. Cách tốt hơn là trả về một kiểu và cho phép khách hàng chọn, hàm tạo mà anh ta muốn gọi:
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);
}
}
}
Dưới đây là một ví dụ sử dụng. @Lars Bohl chỉ hiển thị một cách đăng nhập để có được sự thống nhất về gen thông qua phần mở rộng. @noah chỉ thông qua việc tạo một thể hiện với {}
. Dưới đây là các xét nghiệm để chứng minh cả hai trường hợp:
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);
}
}
Lưu ý: bạn có thể buộc các máy khách TypeReference
luôn sử dụng {}
khi cá thể được tạo bằng cách làm cho lớp này trừu tượng : public abstract class TypeReference<T>
. Tôi đã không làm điều đó, chỉ để hiển thị trường hợp thử nghiệm bị xóa.