在C#中我可以做到这一点:
//This is C#
static T SomeMethod<T>() where T:new()
{
Console.WriteLine("Typeof T: "+typeof(T));
return new T();
}
//And call the method here
SomeMethod<SomeClassName>();
但由于某种原因,我不能让它在Java中工作。
我想要做的是,在超类上创建一个静态方法,这样子类就可以转换为XML。
//This is Java, but doesn't work
public static T fromXml<T>(String xml) {
try {
JAXBContext context = JAXBContext.newInstance(T.class);
Unmarshaller um = context.createUnmarshaller();
return (T)um.unmarshal(new StringReader(xml));
} catch (JAXBException je) {
throw new RuntimeException("Error interpreting XML response", je);
}
}
//Also the call doesn't work...
fromXml<SomeSubObject>("<xml/>");
答案 0 :(得分:51)
public static <T> T fromXml(Class<T> clazz, String xml) {
被称为:
Thing thing = fromXml(Thing.class, xml);
或更明确地说:
Thing thing = MyClass.<Thing>fromXml(Thing.class, xml);
更令人困惑的是,您可以拥有构造函数,这些构造函数都构造泛型类型并且本身具有泛型参数。不记得语法,从未见过它在愤怒中使用过(无论如何,最好使用静态创建方法)。
强制转换(T)
是不安全的,你不能写T.class。所以包括T.class作为参数(如JAXBContext.newInstance
那样)并在类型错误时抛出相关的异常。
public static <T> T fromXml(Class<T> clazz, String xml) {
try {
JAXBContext context = JAXBContext.newInstance(clazz);
Unmarshaller um = context.createUnmarshaller();
Object obj = um.unmarshal(new StringReader(xml));
try {
return clazz.cast(obj);
} catch (ClassCastException exc) {
throw new RelevantException(
"Expected class "+clazz+
" but was "+obj.getClass()
);
}
} catch (JAXBException exc) {
throw new RelevantException(
"Error unmarshalling XML response",
exc
);
}
}
我相信JAXB的下一个版本(在6u14中?)在JAXB
类中有一些方便的方法。
答案 1 :(得分:5)
在Java中,泛型是仅编译时的数据,在运行时会丢失。所以,如果你调用这样的方法,JVM将无法知道T.class
是什么。解决此问题的常用方法是将类实例对象作为参数传递给方法,如下所示:
public static <T> T fromXml(Class<T> clazz, String xml) {
try {
JAXBContext context = JAXBContext.newInstance(clazz);
Unmarshaller um = context.createUnmarshaller();
return (T)um.unmarshal(new StringReader(xml));
} catch (JAXBException je) {
throw new RuntimeException("Error interpreting XML response", je);
}
}
fromXml(SomeSubObject.class, "<xml/>");
答案 2 :(得分:2)
像Java Collections.emptySet()
这样的方法有这样的签名:
public static final <T> Set<T> emptySet()
并且这样称呼:
Set<Foo> foos = Collections.<Foo>emptySet();
Mockito的anyObject()
方法是另一个例子。我个人认为这两种语法都不是很棒。将类型作为方法参数传递起作用但总是感觉很笨拙。以emptySet()
所做的方式提供参数看起来更干净但是方法允许指定类型并不总是显而易见。
答案 3 :(得分:1)
我担心你想要做的事情根本就不适用于Java。无法创建泛型类型的新实例是.NET提供的必备功能之一,而Java完全缺失。这导致了像之前建议的那样的“变通方法”。
查看ArrayList.toArray(T)
作为参考如何实现:基本上,您必须传递对您尝试创建的对象的引用,以便您知道要在运行时实例化的类。这是ArrayList.toArray(T)
的代码:
public <T> T[] toArray(T[] a)
{
if (a.length < size)
{
a = (T[]) java.lang.reflect.Array.
newInstance(a.getClass().getComponentType(), size);
}
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
{
a[size] = null;
}
return a;
}
答案 4 :(得分:1)
目前的答案更安全但技术上已经过时,因为在java7及更高版本中你不需要“Class clazz”参数。该类型是从预期的返回类型推断出来的。你只是得到关于不安全演员的警告。
public class Main {
private static class Dog {
public String toString() { return "dog"; }
}
private static class Cat {
public String toString() { return "cat"; }
}
public static void main(String[] args) {
Cat cat = parse("cat");
Dog dog = parse("dog");
System.out.println("the cat object is a " + cat);
System.out.println("the dog object is a " + dog);
}
private static Object untypedParse(String stringToParse) {
if(stringToParse.equals("dog")) {
return new Dog();
} else if(stringToParse.equals("cat")) {
return new Cat();
} else {
throw new RuntimeException("not expected");
}
}
public static <T> T parse(String stringToParse) {
return (T)untypedParse(stringToParse);
}
}
~/test/generics$ javac Main.java
Note: Main.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
~/test/generics$ java Main
the cat object is a cat
the dog object is a dog