Java中没有泛型参数的泛型方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/590405/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Generic method in Java without generic argument
提问by doekman
In C# I can do actually this:
在 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>();
But for some reason I can't get it to work in Java.
但由于某种原因,我无法让它在 Java 中工作。
The thing I want to do is, to create a static method on a superclass, so the subclasses can be converted to XML.
我想要做的是,在超类上创建一个静态方法,以便子类可以转换为 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/>");
采纳答案by Tom Hawtin - tackline
public static <T> T fromXml(Class<T> clazz, String xml) {
Called as:
称为:
Thing thing = fromXml(Thing.class, xml);
or more explicitly:
或更明确地:
Thing thing = MyClass.<Thing>fromXml(Thing.class, xml);
To be even more confusing you can have constructors that both construct a generic type and have a generic parameter themselves. Can't remember the syntax and have never seen it used in anger (you are probably better off with a static creation method anyway).
更令人困惑的是,您可以拥有既构造泛型类型又自身具有泛型参数的构造函数。不记得语法,也从未见过它在愤怒中使用过(无论如何,使用静态创建方法可能会更好)。
The cast (T)
is unsafe, and you can't write T.class. So include the T.class as an argument (as JAXBContext.newInstance
does) and throw a relevant exception if the type is wrong.
演员表(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
);
}
}
I believe the next version of JAXB (in 6u14?) has some convenience methods for this sort of thing in the JAXB
class.
我相信 JAXB 的下一个版本(在 6u14 中?)在JAXB
类中为此类事情提供了一些方便的方法。
回答by Avi
In Java, generics are compile-time only data, which are lost at run time. So, if you called a method like that, the JVM would have no way of knowing what T.class
was. The normal way to get around this is to pass a class instance object as a parameter to the method, like this:
在 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/>");
回答by Bogdan
I am afraid what you are trying to do will simply not work in Java. Not being able to create new instances of generic types is one of those "must have" features that .NET provided while Java is simply missing. This leads to "workarounds" like those suggested earlier.
Check out the ArrayList.toArray(T)
as a reference how this can be done: essentially you will have to pass a reference to an object that you are trying to create so that you know what class to instantiate at runtime. This is the code from ArrayList.toArray(T)
:
恐怕您尝试做的事情在 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;
}
回答by spaaarky21
Methods like Java's Collections.emptySet()
have a signature like this:
像 Java 这样的方法有这样的Collections.emptySet()
签名:
public static final <T> Set<T> emptySet()
And are called like this:
并被这样调用:
Set<Foo> foos = Collections.<Foo>emptySet();
Mockito's anyObject()
method is another example. I personally don't find either syntax to be very awesome. Passing the type in as a method argument works but always felt kludgy. Providing the parameter in the way that emptySet()
does seems cleaner but it's not always apparent that a method allows a type to be specified.
Mockito 的anyObject()
方法是另一个例子。我个人认为这两种语法都不是非常棒。将类型作为方法参数传入是可行的,但总是感觉很笨拙。以emptySet()
看起来更简洁的方式提供参数,但方法允许指定类型并不总是很明显。
回答by joseph
The current answer is safer but is technically outdated because in java7 and beyond you don't need the "Class clazz" argument. The type is inferred from the expected return type. You just get a warning about unsafe cast.
当前的答案更安全,但在技术上已经过时,因为在 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