我想在java中创建一个Generics Type对象。请建议我如何实现同样的目标。
注意:这似乎是一个微不足道的泛型问题。但我敢打赌......事实并非如此。 :)
假设我有类声明:
public class Abc<T> {
public T getInstanceOfT() {
// I want to create an instance of T and return the same.
}
}
答案 0 :(得分:65)
public class Abc<T> {
public T getInstanceOfT(Class<T> aClass) {
return aClass.newInstance();
}
}
您必须添加异常处理。
你必须在运行时传递实际类型,因为它不是编译后字节代码的一部分,所以没有明确地提供它就无法知道它。
答案 1 :(得分:25)
在您发布的代码中,由于您不知道它是什么类型,因此无法创建T
的实例:
public class Abc<T>
{
public T getInstanceOfT()
{
// There is no way to create an instance of T here
// since we don't know its type
}
}
当然,如果您引用Class<T>
并且T
有默认构造函数,则可以在newInstance()
对象上调用Class
。
如果您继承Abc<T>
,您甚至可以解决类型擦除问题,并且不必传递任何Class<T>
引用:
import java.lang.reflect.ParameterizedType;
public class Abc<T>
{
T getInstanceOfT()
{
ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> type = (Class<T>) superClass.getActualTypeArguments()[0];
try
{
return type.newInstance();
}
catch (Exception e)
{
// Oops, no default constructor
throw new RuntimeException(e);
}
}
public static void main(String[] args)
{
String instance = new SubClass().getInstanceOfT();
System.out.println(instance.getClass());
}
}
class SubClass
extends Abc<String>
{
}
答案 2 :(得分:13)
您所写的内容没有任何意义,Java中的泛型旨在将参数多态的功能添加到对象中。
这是什么意思?这意味着您希望保留未定义的类的某些类型变量,以便能够使用具有许多不同类型的类。
但是你的类型变量 T
是一个在运行时解析的属性,Java编译器将编译你的类证明类型安全而不试图知道什么类型的对象{ {1}}所以它不可能让你在静态方法中使用类型变量。该类型与对象的运行时实例相关联,而T
与类定义相关联,并且在该范围public void static main(..)
并不意味着什么。
如果要在静态方法中使用类型变量,则必须将该方法声明为泛型(这是因为,如模板类的类型变量所解释的那样)与其运行时实例相关,而不是类:
T
这样可行,但当然不能使用class SandBox
{
public static <T> void myMethod()
{
T foobar;
}
}
方法,因为无法以通用方式调用它。
编辑:问题在于,由于类型擦除,只编译了一个泛型类并将其传递给JVM。类型检查器只是检查代码是否安全,然后证明它被丢弃了各种通用信息。
要实例化main
,您需要知道T
的类型,但它可以同时包含多种类型,因此只需要最少量反射的一个解决方案就是使用{{ 1}}实例化新对象:
T
当然这意味着您想要实例化的类型:
要与不同类型的构造函数一起操作,您必须深入研究反射。
答案 3 :(得分:5)
您需要静态获取类型信息。试试这个:
public class Abc<T> {
private Class<T> clazz;
public Abc(Class<T> clazz) {
this.clazz = clazz;
}
public T getInstanceOfT()
throws InstantiationException, IllegalAccessException {
return clazz.newInstance();
}
}
使用它:
Abc<String> abc = new Abc<String>(String.class);
abc.getInstanceOfT();
根据您的需要,您可能希望改为使用Class<? extends T>
。
答案 4 :(得分:4)
使其发挥作用的唯一方法是使用Reified Generics。这在Java中不受支持(但它已经计划用于Java 7,但已被推迟)。例如,在C#中,假设T
具有默认构造函数,则支持它。您甚至可以通过typeof(T)
获取运行时类型,并通过Type.GetConstructor()
获取构造函数。我不做C#所以语法可能无效,但它大致如下:
public class Foo<T> where T:new() {
public void foo() {
T t = new T();
}
}
Java中最好的“解决方法”是传递Class<T>
作为方法参数,而不是已经指出的几个答案。
答案 5 :(得分:3)
首先,您不能在静态main方法中访问类型参数T
,只能访问非静态类成员(在本例中)。
其次,您无法实例化T
,因为Java使用 Type Erasure 实现泛型。几乎所有通用信息都在编译时被删除。
基本上,你不能这样做:
T member = new T();
以下是关于generics的精彩教程。
答案 6 :(得分:2)
您似乎并不了解Generics的工作原理。 你可能想看看http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html 基本上你可以做的就是
public class Abc<T>
{
T someGenericThing;
public Abc(){}
public T getSomeGenericThing()
{
return someGenericThing;
}
public static void main(String[] args)
{
// create an instance of "Abc of String"
Abc<String> stringAbc = new Abc<String>();
String test = stringAbc.getSomeGenericThing();
}
}
答案 7 :(得分:1)
我正在使用以下方法实现相同的目标。
public class Abc<T>
{
T myvar;
public T getInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException
{
return clazz.newInstance();
}
}
我试图找到一种更好的方法来实现同样的目标。
不可能吗?
答案 8 :(得分:1)
受@martin's answer的启发,我编写了一个帮助程序类,允许我解决类型擦除问题。使用这个类(以及一个有点丑陋的技巧)我可以用模板类型创建一个新实例:
public abstract class C_TestClass<T > {
T createTemplateInstance() {
return C_GenericsHelper.createTemplateInstance( this, 0 );
}
public static void main( String[] args ) {
ArrayList<String > list =
new C_TestClass<ArrayList<String > >(){}.createTemplateInstance();
}
}
这里的丑陋技巧是创建类abstract
,因此强制类的用户对其进行子类型化。在这里,我通过在调用构造函数之后附加{}
来对其进行子类化。这定义了一个新的匿名类并创建了它的实例。
一旦使用具体的模板类型对泛型类进行了子类型化,我就能够检索模板类型。
public class C_GenericsHelper {
/**
* @param object instance of a class that is a subclass of a generic class
* @param index index of the generic type that should be instantiated
* @return new instance of T (created by calling the default constructor)
* @throws RuntimeException if T has no accessible default constructor
*/
@SuppressWarnings( "unchecked" )
public static <T> T createTemplateInstance( Object object, int index ) {
ParameterizedType superClass =
(ParameterizedType )object.getClass().getGenericSuperclass();
Type type = superClass.getActualTypeArguments()[ index ];
Class<T > instanceType;
if( type instanceof ParameterizedType ) {
instanceType = (Class<T > )( (ParameterizedType )type ).getRawType();
}
else {
instanceType = (Class<T > )type;
}
try {
return instanceType.newInstance();
}
catch( Exception e ) {
throw new RuntimeException( e );
}
}
}
答案 9 :(得分:0)
看起来你正在尝试创建一个类作为通用的应用程序的入口点,并且不起作用...... JVM将不知道它应该在什么类型时使用它在你启动应用程序时被实例化。
然而,如果这是更一般的情况,那么你正在寻找的东西:
public MyGeneric<MyChoiceOfType> getMeAGenericObject(){
return new MyGeneric<MyChoiceOfType>();
}
或者也许:
MyGeneric<String> objMyObject = new MyGeneric<String>();
答案 10 :(得分:0)
当你真的必须这样做时,有很多方法可以解决这个问题。
这是我觉得非常有用的变换方法的一个例子;并提供了一种确定泛型的具体类的方法。
此方法接受一组对象作为输入,并返回一个数组,其中每个元素都是在输入集合中的每个对象上调用字段getter的结果。例如,假设您有一个List<People>
,并且您希望String[]
包含每个人的姓氏。
getter返回的字段值的类型由泛型E
指定,我需要实例化一个类型E[]
的数组来存储返回值。
该方法本身有点难看,但您编写的使用它的代码可以更清晰。
请注意,此技术仅在输入参数中的某个位置存在时,其类型与返回类型匹配,并且您可以确定地将其计算出来。如果输入参数(或其子对象)的具体类可以告诉您关于泛型的任何信息,那么这种技术将不起作用。
public <E> E[] array (Collection c) {
if (c == null) return null;
if (c.isEmpty()) return (E[]) EMPTY_OBJECT_ARRAY;
final List<E> collect = (List<E>) CollectionUtils.collect(c, this);
final Class<E> elementType = (Class<E>) ReflectionUtil.getterType(c.iterator().next(), field);
return collect.toArray((E[]) Array.newInstance(elementType, collect.size()));
}
答案 11 :(得分:-3)
Abc<String> abcInstance = new Abc<String> ();
..例如