是否可以在Java中创建泛型类型的实例?我正在考虑基于我所看到的答案是no(由于类型擦除),但如果有人能看到我缺少的东西,我会感兴趣:
class SomeContainer<E>
{
E createContents()
{
return what???
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:事实证明,超级类型标记可用于解决我的问题,但它需要大量基于反射的代码,如下面的一些答案所示.
我会把这个开放一段时间,看看是否有人提出了与Ian Robertson的Artima文章截然不同的任何东西.
Jus*_*udd 325
你是对的.你做不到new E().但你可以改成它
private static class SomeContainer<E> {
E createContents(Class<E> clazz) {
return clazz.newInstance();
}
}
Run Code Online (Sandbox Code Playgroud)
这是一种痛苦.但它的确有效.将其包装在工厂模式中使其更容易忍受.
noa*_*oah 124
Dunno如果这有帮助,但是当您子类化(包括匿名)泛型类型时,类型信息可通过反射获得.例如,
public abstract class Foo<E> {
public E instance;
public Foo() throws Exception {
instance = ((Class)((ParameterizedType)this.getClass().
getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
...
}
}
Run Code Online (Sandbox Code Playgroud)
所以,当你继承Foo时,你得到一个Bar实例,例如,
// notice that this in anonymous subclass of Foo
assert( new Foo<Bar>() {}.instance instanceof Bar );
Run Code Online (Sandbox Code Playgroud)
但这是很多工作,只适用于子类.虽然可以派上用场.
Dan*_*den 101
在Java 8中,您可以使用Supplier功能界面轻松实现此目的:
class SomeContainer<E> {
private Supplier<E> supplier;
SomeContainer(Supplier<E> supplier) {
this.supplier = supplier;
}
E createContents() {
return supplier.get();
}
}
Run Code Online (Sandbox Code Playgroud)
你可以这样构造这个类:
SomeContainer<String> stringContainer = new SomeContainer<>(String::new);
Run Code Online (Sandbox Code Playgroud)
该String::new行的语法是构造函数引用.
如果构造函数接受参数,则可以使用lambda表达式:
SomeContainer<BigInteger> bigIntegerContainer
= new SomeContainer<>(() -> new BigInteger(1));
Run Code Online (Sandbox Code Playgroud)
Tom*_*ine 87
你需要某种抽象的工厂来传递:
interface Factory<E> {
E create();
}
class SomeContainer<E> {
private final Factory<E> factory;
SomeContainer(Factory<E> factory) {
this.factory = factory;
}
E createContents() {
return factory.create();
}
}
Run Code Online (Sandbox Code Playgroud)
Lar*_*ohl 24
package org.foo.com;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* Basically the same answer as noah's.
*/
public class Home<E>
{
@SuppressWarnings ("unchecked")
public Class<E> getTypeParameterClass()
{
Type type = getClass().getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) type;
return (Class<E>) paramType.getActualTypeArguments()[0];
}
private static class StringHome extends Home<String>
{
}
private static class StringBuilderHome extends Home<StringBuilder>
{
}
private static class StringBufferHome extends Home<StringBuffer>
{
}
/**
* This prints "String", "StringBuilder" and "StringBuffer"
*/
public static void main(String[] args) throws InstantiationException, IllegalAccessException
{
Object object0 = new StringHome().getTypeParameterClass().newInstance();
Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
System.out.println(object0.getClass().getSimpleName());
System.out.println(object1.getClass().getSimpleName());
System.out.println(object2.getClass().getSimpleName());
}
}
Run Code Online (Sandbox Code Playgroud)
R2D*_*2M2 21
如果在泛型类中需要一个类型参数的新实例,那么让构造函数需要它的类......
public final class Foo<T> {
private Class<T> typeArgumentClass;
public Foo(Class<T> typeArgumentClass) {
this.typeArgumentClass = typeArgumentClass;
}
public void doSomethingThatRequiresNewT() throws Exception {
T myNewT = typeArgumentClass.newInstance();
...
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
Foo<Bar> barFoo = new Foo<Bar>(Bar.class);
Foo<Etc> etcFoo = new Foo<Etc>(Etc.class);
Run Code Online (Sandbox Code Playgroud)
优点:
缺点:
Foo<L>证明.对于初学者... newInstance()如果类型参数类没有默认构造函数,则会抛出一个摇摆器.尽管如此,这确实适用于所有已知的解决方案.小智 21
你现在可以这样做,它不需要一堆反射代码.
import com.google.common.reflect.TypeToken;
public class Q26289147
{
public static void main(final String[] args) throws IllegalAccessException, InstantiationException
{
final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {};
final String string = (String) smpc.type.getRawType().newInstance();
System.out.format("string = \"%s\"",string);
}
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
}
Run Code Online (Sandbox Code Playgroud)
当然,如果你需要调用需要一些反射的构造函数,但是这个文档记录很清楚,这个技巧不是!
Ing*_*ngo 12
想想一个更实用的方法:不是从无到有创造一些E(这显然是代码气味),而是传递一个知道如何创建一个的函数,即
E createContents(Callable<E> makeone) {
return makeone.call(); // most simple case clearly not that useful
}
Run Code Online (Sandbox Code Playgroud)
您无法创建类型参数的实例.例如,以下代码导致编译时错误:
public static <E> void append(List<E> list) {
E elem = new E(); // compile-time error
list.add(elem);
}
Run Code Online (Sandbox Code Playgroud)
作为解决方法,您可以通过反射创建类型参数的对象:
public static <E> void append(List<E> list, Class<E> cls) throws Exception {
E elem = cls.newInstance(); // OK
list.add(elem);
}
Run Code Online (Sandbox Code Playgroud)
您可以按如下方式调用append方法:
List<String> ls = new ArrayList<>();
append(ls, String.class);
Run Code Online (Sandbox Code Playgroud)
当您在编译时使用 E 时,您并不真正关心实际的泛型类型“E”(您使用反射或使用泛型类型的基类),因此让子类提供 E 的实例。
abstract class SomeContainer<E>
{
abstract protected E createContents();
public void doWork(){
E obj = createContents();
// Do the work with E
}
}
class BlackContainer extends SomeContainer<Black>{
protected Black createContents() {
return new Black();
}
}
Run Code Online (Sandbox Code Playgroud)
这是我提出的一个选项,它可能会有所帮助:
public static class Container<E> {
private Class<E> clazz;
public Container(Class<E> clazz) {
this.clazz = clazz;
}
public E createContents() throws Exception {
return clazz.newInstance();
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:或者你可以使用这个构造函数(但它需要一个E实例):
@SuppressWarnings("unchecked")
public Container(E instance) {
this.clazz = (Class<E>) instance.getClass();
}
Run Code Online (Sandbox Code Playgroud)
如果你不希望在实例化过程中两次输入类名,如:
new SomeContainer<SomeType>(SomeType.class);
Run Code Online (Sandbox Code Playgroud)
您可以使用工厂方法:
<E> SomeContainer<E> createContainer(Class<E> class);
Run Code Online (Sandbox Code Playgroud)
像:
public class Container<E> {
public static <E> Container<E> create(Class<E> c) {
return new Container<E>(c);
}
Class<E> c;
public Container(Class<E> c) {
super();
this.c = c;
}
public E createInstance()
throws InstantiationException,
IllegalAccessException {
return c.newInstance();
}
}
Run Code Online (Sandbox Code Playgroud)
Java不幸地不允许你想做什么.查看官方解决方法:
您无法创建类型参数的实例.例如,以下代码导致编译时错误:
public static <E> void append(List<E> list) {
E elem = new E(); // compile-time error
list.add(elem);
}
Run Code Online (Sandbox Code Playgroud)
作为解决方法,您可以通过反射创建类型参数的对象:
public static <E> void append(List<E> list, Class<E> cls) throws Exception {
E elem = cls.newInstance(); // OK
list.add(elem);
}
Run Code Online (Sandbox Code Playgroud)
您可以按如下方式调用append方法:
List<String> ls = new ArrayList<>();
append(ls, String.class);
Run Code Online (Sandbox Code Playgroud)
小智 5
您可以使用:
Class.forName(String).getConstructor(arguments types).newInstance(arguments)
Run Code Online (Sandbox Code Playgroud)
但是您需要提供确切的类名,包括包,例如。java.io.FileInputStream. 我用它来创建一个数学表达式解析器。
希望这不是太晚的帮助!!!
Java 是类型安全的,这意味着只有对象才能创建实例。
就我而言,我无法将参数传递给该createContents方法。我的解决方案是使用扩展,与下面的答案不同。
private static class SomeContainer<E extends Object> {
E e;
E createContents() throws Exception{
return (E) e.getClass().getDeclaredConstructor().newInstance();
}
}
Run Code Online (Sandbox Code Playgroud)
这是我无法传递参数的示例。
public class SomeContainer<E extends Object> {
E object;
void resetObject throws Exception{
object = (E) object.getClass().getDeclaredConstructor().newInstance();
}
}
Run Code Online (Sandbox Code Playgroud)
如果使用无对象类型扩展泛型类,则使用反射会创建运行时错误。要将泛型类型扩展为对象,请将此错误转换为编译时错误。
| 归档时间: |
|
| 查看次数: |
432100 次 |
| 最近记录: |