Java中等效于C#的MakeGenericType以创建未知泛型类型的实例

huangapple go评论76阅读模式
英文:

Java equivalent of c#'s MakeGenericType to create instance of unknown generic type

问题

自从我接触过Java已经有很长时间了。但是根据以下的C#语法,是否可能在Java中创建一个未知的泛型类型的实例呢?

public void Send(Object message)
{
    Class<?> requestType = message.getClass();
    ReceiverWrapper<?> receiverInstance = (ReceiverWrapper<?>)
            Class.forName("ReceiverWrapper").getDeclaredConstructor().newInstance();

    // 对接收器进行一些操作...
}

所以想法是,Object message 是类型为 Message1。我想创建一个 ReceiverWrapper<Message1> 的实例。

我不想在方法签名中使用泛型 - 就像 public <T> void Send(T message),因为我已经理解这点。我想知道是否可以从Object中提取类型信息,并根据该类型创建一个泛型。

英文:

Its been a long time since I have touched Java. But given the following C# syntax, is it possible to create an unknown instance of a generic type in java?

public void Send(object message)
{
    Type requestType = message.GetType();
    ReceiverWrapper receiverInstance = (ReceiverWrapper)
            Activator.CreateInstance(typeof(ReceiverWrapper&lt;&gt;).MakeGenericType(requestType));

    // do something with the receiver...
}

So the idea being Object message is of type Message1. I want to create an instance of ReceiverWrapper&lt;Message1&gt;.

I don't want to use generics in method signature - ala public &lt;T&gt; void Send(T message) as I already understand this in Java. I am curious whether we can extract type information from Object and create a generic based on that type.

答案1

得分: 3

Sure, here is the translated content:

> 我想要创建一个 ReceiverWrapper&lt;Message1&gt; 的实例。

在Java中没有这样的东西。只有 ReceiverWrapper。Java中的泛型主要是在编译时使用的。例如,如果你有一个类型为 List&lt;String&gt; 的字段,你可以通过反射确定它 - 但是一个 对象 不是 ArrayList&lt;String&gt; 的实例,它只是 ArrayList 的实例。

英文:

> I want to create an instance of ReceiverWrapper&lt;Message1&gt;.

There's no such thing in Java. There's just ReceiverWrapper. Java generics are mostly a compile-time artifact. For example, if you have a field which is of type List&lt;String&gt; you can determine that with reflection - but an object isn't an instance of ArrayList&lt;String&gt;, it's just an instance of ArrayList.

答案2

得分: 2

这里有一些需要解释的事情,首先 var 不是一种通用类型。Var 会在编译时被实际类型替换,所以如果你想使用泛型,你可能不应该使用 var。

public <T> void Send(T message)
{
    ReceiverWrapper<T> receiverInstance = new ReceiverWrapper<>();

    // 对接收器进行一些操作...
}

我不太确定你想要做什么,但类似这样的操作可能可以满足你的需求。

英文:

There are a few things to unpack here, first of all var is not a generic type. Var will be replaced by an actual type at compile time so if you want to use generics you probably shouldnt use var.

public &lt;T&gt; void Send(T message)
{
    ReceiverWrapper&lt;T&gt; receiverInstance = new ReceiverWrapper&lt;&gt;();

// do something with the receiver...
}

I'm not entirely sure what you want to do with it but something like this?

答案3

得分: 1

&hellip;在Java中是否有可能创建未知类型的泛型实例?&hellip;

是的。是可能的。但只有在可以保证某些前提条件的情况下才能实现(请参阅代码中的注释)。

我在下面的代码片段中概述了这种技术的要点。可以在在线上运行实际实验&hellip;

{
    ...
    Optional&lt; Object &gt; parameterizedType ...

    TypeVariable&lt; ? &gt; typeVars[ ] ...

    /* 前提条件1:目标必须是泛型类 */
    if( typeVars.length == 0 ){ throw new NotAGenericTypeException( ); }
  
    /* 前提条件2:目标必须公开一个构造函数,该构造函数声明为采用参数,
     * 参数的类型与泛型类的类型参数部分中声明的类型相同 */
    if( isConstructorCompatible ( target, typeVars ) ){
        
        try{ 
            parameterizedType = Optional.of( target.getClass( ).getConstructors( )[0].newInstance( typeArgs ) );
        } catch( ReflectiveOperationException roe ){ roe.printStackTrace( ); }
    }
    
    return parameterizedType;
}

&hellip;所以Object message的类型是Message1。我想创建一个ReceiverWrapper&lt;Message1&gt;的实例&hellip;

尽管你的问题中的 Send(Object) 方法返回 void,为了使我的概念验证更具测试性,我的实验性实现返回了参数化类型的实例&hellip;

...
public Optional&lt; Object &gt; send( Object message ){ 
    return parameterizer.parameterize( ReceiverWrapper.class, message );    
}
...

使用示例&hellip;

...
/* 设置@Andez使用案例的测试 */
Object sos = new Message( "太空之友,你们好吗?你们吃饭了吗?如果有时间,请来访问我们。" );
    
/* 正在测试的类 */
ProofOfConcept voyager = new ProofOfConcept( );
    
/* 执行测试的方法 */
Optional&lt; Object &gt; type  voyager.send( sos );
    
/* 准备在之前的voyager.send( )调用中进行的参数化的结果 */
ReceiverWrapper&lt; Message &gt; oumuamua = type.map( raw -&gt; (ReceiverWrapper&lt; Message &gt;)raw ).orElseThrow( AssertionError::new );
    
/* 利用编译器的类型检查 */
Message fromSpace = oumuamua.getMessage( );
    
/* 验证send( )返回的预期类型是类型参数的类型 */
assert ((Object)fromSpace) instanceof Message : "实验失败";
...

实验结果&hellip;

Message [ payload: 太空之友,你们好吗?你们吃饭了吗?如果有时间,请来访问我们。 ]
             实验成功

他们曾说这是不可能的 Java中等效于C#的MakeGenericType以创建未知泛型类型的实例

英文:

> „&hellip;is it possible to create an unknown instance of a generic type in java?&hellip;

Yes. It is possible. But only if certain preconditions can be guaranteed (see the comments in the code).

I outline the gist of the technique with the snippets below. The working experiment can be run online&hellip;

{
    ...
    Optional&lt; Object &gt; parameterizedType ...

    TypeVariable&lt; ? &gt; typeVars[ ] ...

    /* Precondition #1: target MUST be a generic class */
    if( typeVars.length == 0 ){ throw new NotAGenericTypeException( ); }
  
    /* Precondition #2: target MUST expose a constructor that&#39;s declared to take arguments
     * whose type is that of those declared in the generic class&#39;s type parameter section */
    if( isConstructorCompatible ( target, typeVars ) ){
        
        try{ 
            parameterizedType = Optional.of( target.getClass( ).getConstructors( )[0].newInstance( typeArgs ) );
        } catch( ReflectiveOperationException roe ){ roe.printStackTrace( ); }
    }
    
    return parameterizedType;
}

> „&hellip;So the idea being Object message is of type Message1. I want to create an instance of ReceiverWrapper&lt;Message1&gt;&hellip;

Although your question's Send(Object) method returns void, to make my proof of concept more testable my experimental implementation returns an instance of the parameterized type&hellip;

...
public Optional&lt; Object &gt; send( Object message ){ 
    return parameterizer.parameterize( ReceiverWrapper.class, message );    
}
...

Usage example&hellip;

...
/* Set up a test of @Andez&#39;s use case  */
Object sos = new Message( &quot;Friends of space, how are you all? Have you eaten yet? Come visit us if you have time.&quot; );
    
/* The class under test */
ProofOfConcept voyager = new ProofOfConcept( );
    
/* Exercise the method under test */
Optional&lt; Object &gt; type  voyager.send( sos );
    
/* Prepare the outcome of the parameterization done in the previous voyager.send( ) call */
ReceiverWrapper&lt; Message &gt; oumuamua = type.map( raw -&gt; (ReceiverWrapper&lt; Message &gt;)raw ).orElseThrow( AssertionError::new );
    
/* Leverage the compiler&#39;s type checking */
Message fromSpace = oumuamua.getMessage( );
    
/* Verify that the expected type returned by send( ) is the type of the type argument */
assert ((Object)fromSpace) instanceof Message : &quot;EXPERIMENT FAILED&quot;;
...

Results of the experiment&hellip;

Message [ payload: Friends of space, how are you all? Have you eaten yet? Come visit us if you have time. ]
             EXPERIMENT SUCCESSFUL

And they said it couldn't be done Java中等效于C#的MakeGenericType以创建未知泛型类型的实例

huangapple
  • 本文由 发表于 2020年8月19日 17:33:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/63484040.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定