如何在Java中使用Class <T>?
在这个问题上 ,有很多关于generics的内容以及他们在幕后做了些什么,所以我们都知道Vector<int[]>
是整数数组的向量,而HashTable<String, Person>
是一个表的键是string和值Person
。 但是,什么让我难过的是Class<>
的用法。
java class Class
应该也是一个模板名,(或者我被eclipse中的黄色下划线所告知)。 我不明白我应该放在那里。 Class
对象的全部重点是当你没有完全掌握一个对象的信息,reflection等等。 为什么它会让我指定Class
对象将保持哪个类? 我显然不知道,或者我不会使用Class
对象,我会使用具体的一个。
使用类的基因纯化版本,可以让你等等写东西
Class<? extends Collection> someCollectionClass = someMethod();
然后你可以确定你接收到的Class对象inheritance了Collection
,而这个类的一个实例将是(至less)一个Collection。
我们所知道的是“ 任何类的所有实例共享该types类的相同java.lang.Class对象 ”
例如)
Student a = new Student(); Student b = new Student();
那么a.getClass() == b.getClass()
是真的。
现在假设
Teacher t = new Teacher();
没有generics下面是可能的。
Class studentClassRef = t.getClass();
但现在这是错误的..?
例如) public void printStudentClassInfo(Class studentClassRef) {}
可以用Teacher.class
这可以避免使用generics。
Class<Student> studentClassRef = t.getClass(); //Compilation error.
现在什么是T? T是types参数(也称为typesvariables); 用尖括号(<>)分隔,跟在类名之后。
T只是一个符号,就像在类文件写入时声明的variables名(可以是任何名字)。 之后T将被replace
初始化期间有效的类名( HashMap<String> map = new HashMap<String>();
)
例如) class name<T1, T2, ..., Tn>
所以Class<T>
表示特定类types“ T
”的类对象。
假设你的类方法必须使用如下的未知types参数
/** * Generic version of the Car class. * @param <T> the type of the value */ public class Car<T> { // T stands for "Type" private T t; public void set(T t) { this.t = t; } public T get() { return t; } }
这里T可以用作String
types作为CarName
OR T可以作为Integer
types作为modelNumber ,
OR T可以用作Object
types作为有效的汽车实例 。
现在在这里上面是简单的POJO,可以在运行时使用不同的POJO。
集合例如)List,Set,Hashmap是最好的例子,它们可以根据T的声明使用不同的对象,但是一旦我们声明T为String
例如) HashMap<String> map = new HashMap<String>();
那么它只会接受String类的实例对象。
通用方法
generics方法是引入自己的types参数的方法。 这与声明genericstypes相似,但是types参数的作用域仅限于声明它的方法。 允许使用静态和非静态generics方法,以及generics类构造函数。
generics方法的语法包括一个types参数,尖括号内,并出现在方法的返回types之前。 对于generics方法,types参数部分必须出现在方法的返回types之前。
class Util { // Generic static method public static <K, V, Z, Y> boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) { return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue()); } } class Pair<K, V> { private K key; private V value; }
这里<K, V, Z, Y>
是在方法参数中使用的types的声明,它应该在返回types之前是boolean
。
在下面; types声明<T>
在方法级别是不需要的,因为它已经在类级别声明了。
class MyClass<T> { private T myMethod(T a){ return a; } }
但是下面是错误的,因为类级别types参数K,V,Z和Y不能在静态上下文中使用(这里是静态方法)。
class Util <K, V, Z, Y>{ // Generic static method public static boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) { return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue()); } }
其他有效的情景是
class MyClass<T> { //Type declaration <T> already done at class level private T myMethod(T a){ return a; } //<T> is overriding the T declared at Class level; //So There is no ClassCastException though a is not the type of T declared at MyClass<T>. private <T> T myMethod1(Object a){ return (T) a; } //Runtime ClassCastException will be thrown if a is not the type T (MyClass<T>). private T myMethod1(Object a){ return (T) a; } // No ClassCastException // MyClass<String> obj= new MyClass<String>(); // obj.myMethod2(Integer.valueOf("1")); // Since type T is redefined at this method level. private <T> T myMethod2(T a){ return a; } // No ClassCastException for the below // MyClass<String> o= new MyClass<String>(); // o.myMethod3(Integer.valueOf("1").getClass()) // Since <T> is undefined within this method; // And MyClass<T> don't have impact here private <T> T myMethod3(Class a){ return (T) a; } // ClassCastException for o.myMethod3(Integer.valueOf("1").getClass()) // Should be o.myMethod3(String.valueOf("1").getClass()) private T myMethod3(Class a){ return (T) a; } // Class<T> a :: a is Class object of type T //<T> is overriding of class level type declaration; private <T> Class<T> myMethod4(Class<T> a){ return a; } }
最后,静态方法总是需要显式的<T>
声明; 它不会从类级别的Class<T>
派生。 这是因为级别T与实例绑定在一起。
另请阅读generics的限制
从Java文档:
更令人惊讶的是,class级已经被基因化。 类文字现在用作types标记,提供运行时和编译时types信息。 这样可以在新的AnnotatedElement接口中启用由getAnnotation方法示例的静态工厂样式:
<T extends Annotation> T getAnnotation(Class<T> annotationType);
这是一个通用的方法。 它从它的参数中推断出它的types参数T的值,并返回一个适当的T实例,如下面的片段所示:
Author a = Othello.class.getAnnotation(Author.class);
仿制药之前,你必须把结果投给作者。 你也没有办法让编译器检查实际参数是否表示Annotation的子类。 […]
那么,我从来没有使用这种东西。 任何人?
当我创build服务registry查找时,发现class<T>
很有用。 例如
<T> T getService(Class<T> serviceClass) { ... }
正如其他答案所指出的那样,为什么这个class
是通用的,有很多很好的理由。 然而,有很多次你没有办法知道用Class<T>
使用的genericstypes。 在这些情况下,你可以简单地忽略黄色的日食警告,或者你可以使用Class<?>
…这就是我的做法;)
在@Kire Haglin的回答之后,在JAXB解组的文档中可以看到generics方法的另一个例子:
public <T> T unmarshal( Class<T> docClass, InputStream inputStream ) throws JAXBException { String packageName = docClass.getPackage().getName(); JAXBContext jc = JAXBContext.newInstance( packageName ); Unmarshaller u = jc.createUnmarshaller(); JAXBElement<T> doc = (JAXBElement<T>)u.unmarshal( inputStream ); return doc.getValue(); }
这允许unmarshal
返回任意JAXB内容树types的文档。
你经常想用Class
来使用通配符。 例如, Class<? extends JComponent>
Class<? extends JComponent>
,将允许您指定该类是JComponent
某个子类。 如果您从Class.forName
检索到Class
实例,则可以在尝试构build实例之前使用Class.asSubclass
进行Class.asSubclass
转换。
只是抛出另一个例子,Class( Class<T>
)的通用版本允许编写如下所示的generics函数。
public static <T extends Enum<T>>Optional<T> optionalFromString( @NotNull Class<T> clazz, String name ) { return Optional<T> opt = Optional.ofNullable(name) .map(String::trim) .filter(StringUtils::isNotBlank) .map(String::toUpperCase) .flatMap(n -> { try { return Optional.of(Enum.valueOf(clazz, n)); } catch (Exception e) { return Optional.empty(); } }); }
这一开始是令人困惑的。 但是在下面的情况下它有帮助:
class SomeAction implements Action { } // Later in the code. Class<Action> actionClass = Class.forName("SomeAction"); Action action = actionClass.newInstance(); // Notice you get an Action instance, there was no need to cast.
只要使用牛肉类:
public <T> T beefmarshal( Class<beef> beefClass, InputBeef inputBeef ) throws JAXBException { String packageName = docClass.getPackage().getBeef(); JAXBContext beef = JAXBContext.newInstance( packageName ); Unmarshaller u = beef.createBeef(); JAXBElement<T> doc = (JAXBElement<T>)u.beefmarshal( inputBeef ); return doc.getBeef(); }