Java反射在运行时实例化泛型

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

Java Reflection to Instantiate Generics at Runtime

问题

我试图将我的应用程序与Bukkit - 一个Minecraft服务器API集成。

基本上,我正在尝试弄清楚如何使这样的功能工作:

Reflections reflections = new Reflections("com.mycompany");    
Set<Class<? extends BlockEvent>> classes = reflections.getSubTypesOf(BlockEvent.class);
for (Class<? extends BlockEvent> clazz : classes) {
    getServer().getPluginManager().registerEvents(new BlockListener<clazz>(), this);
}

我想要为所有继承BlockEvent的事件类型注册监听器。通过将Listener的实现传递给Bukkit API的PluginManager中的registerEvents(Listener, Plugin)方法来注册事件。显然,clazz不是一种类型,不能用作此类。

这是泛型类:

public class BlockListener<T extends BlockEvent> implements Listener {
  @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
  void onBlock(T event) {
    System.out.println("Block Event");
    Bukkit.getPluginManager().callEvent(new BlockChangeEvent(event.getBlock()));
  }
}

更新

public Map<Class<? extends Event>, Set<RegisteredListener>> createRegisteredListeners(@NotNull Listener listener, @NotNull final Plugin plugin) {
    Validate.notNull(plugin, "Plugin can not be null");
    Validate.notNull(listener, "Listener can not be null");

    boolean useTimings = server.getPluginManager().useTimings();
    Map<Class<? extends Event>, Set<RegisteredListener>> ret = new HashMap<Class<? extends Event>, Set<RegisteredListener>>();
    Set<Method> methods;
    try {
        Method[] publicMethods = listener.getClass().getMethods();
        Method[] privateMethods = listener.getClass().getDeclaredMethods();
        methods = new HashSet<Method>(publicMethods.length + privateMethods.length, 1.0f);
        for (Method method : publicMethods) {
            methods.add(method);
        }
        for (Method method : privateMethods) {
            methods.add(method);
        }
    } catch (NoClassDefFoundError e) {
        plugin.getLogger().severe("Plugin " + plugin.getDescription().getFullName() + " has failed to register events for " + listener.getClass() + " because " + e.getMessage() + " does not exist.");
        return ret;
    }

    for (final Method method : methods) {
        final EventHandler eh = method.getAnnotation(EventHandler.class);
        if (eh == null) continue;
        // Do not register bridge or synthetic methods to avoid event duplication
        // Fixes SPIGOT-893
        if (method.isBridge() || method.isSynthetic()) {
            continue;
        }
        final Class<?> checkClass;
        if (method.getParameterTypes().length != 1 || !Event.class.isAssignableFrom(checkClass = method.getParameterTypes()[0])) {
            plugin.getLogger().severe(plugin.getDescription().getFullName() + " attempted to register an invalid EventHandler method signature \"" + method.toGenericString() + "\" in " + listener.getClass());
            continue;
        }
        final Class<? extends Event> eventClass = checkClass.asSubclass(Event.class);
        method.setAccessible(true);
        Set<RegisteredListener> eventSet = ret.get(eventClass);
        if (eventSet == null) {
            eventSet = new HashSet<RegisteredListener>();
            ret.put(eventClass, eventSet);
        }

        for (Class<?> clazz = eventClass; Event.class.isAssignableFrom(clazz); clazz = clazz.getSuperclass()) {
            // This loop checks for extending deprecated events
            if (clazz.getAnnotation(Deprecated.class) != null) {
                Warning warning = clazz.getAnnotation(Warning.class);
                WarningState warningState = server.getWarningState();
                if (!warningState.printFor(warning)) {
                    break;
                }
                plugin.getLogger().log(
                        Level.WARNING,
                        String.format(
                                "\"%s\" has registered a listener for %s on method \"%s\", but the event is Deprecated. \"%s\"; please notify the authors %s.",
                                plugin.getDescription().getFullName(),
                                clazz.getName(),
                                method.toGenericString(),
                                (warning != null && warning.reason().length() != 0) ? warning.reason() : "Server performance will be affected",
                                Arrays.toString(plugin.getDescription().getAuthors().toArray())),
                        warningState == WarningState.ON ? new AuthorNagException(null) : null);
                break;
            }
        }

        final CustomTimingsHandler timings = new CustomTimingsHandler("Plugin: " + plugin.getDescription().getFullName() + " Event: " + listener.getClass().getName() + "::" + method.getName() + "(" + eventClass.getSimpleName() + ")", pluginParentTimer); // Spigot
        EventExecutor executor = new EventExecutor() {
            @Override
            public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
                try {
                    if (!eventClass.isAssignableFrom(event.getClass())) {
                        return;
                    }
                    // Spigot start
                    boolean isAsync = event.isAsynchronous();
                    if (!isAsync) timings.startTiming();
                    method.invoke(listener, event);
                    if (!isAsync) timings.stopTiming();
                    // Spigot end
                } catch (InvocationTargetException ex) {
                    throw new EventException(ex.getCause());
                } catch (Throwable t) {
                    throw new EventException(t);
                }
            }
        };
        if (false) { // Spigot - RL handles useTimings check now
            eventSet.add(new TimedRegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled()));
        } else {
            eventSet.add(new RegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled()));
        }
    }
    return ret;
}

具体来说

method.getParameterTypes()[0]

无法与泛型一起工作。

英文:

I'm trying to integrate my application with Bukkit - A Minecraft Server API.

Basically, I'm trying to figure out how could I get functionality like this to work:

Reflections reflections = new Reflections(&quot;com.mycompany&quot;);    
Set&lt;Class&lt;? extends BlockEvent&gt;&gt; classes = reflections.getSubTypesOf(BlockEvent.class);
for (Class&lt;? extends BlockEvent&gt; clazz : classes) {
    getServer().getPluginManager().registerEvents(new BlockListener&lt;clazz&gt;(), this);
}

I want to register a listener for all event types that extend BlockEvent. Events are registered by passing an implemention of Listener into the registerEvents(Listener, Plugin) method exposed by the Bukkit API's PluginManager. Obviously clazz is not a type and cannot be used as such.

Here is the generic class:

public class BlockListener&lt;T extends BlockEvent&gt; implements Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
void onBlock(T event) {
System.out.println(&quot;Block Event&quot;);
Bukkit.getPluginManager().callEvent(new BlockChangeEvent(event.getBlock()));
}
}

Update

    public Map&lt;Class&lt;? extends Event&gt;, Set&lt;RegisteredListener&gt;&gt; createRegisteredListeners(@NotNull Listener listener, @NotNull final Plugin plugin) {
Validate.notNull(plugin, &quot;Plugin can not be null&quot;);
Validate.notNull(listener, &quot;Listener can not be null&quot;);
boolean useTimings = server.getPluginManager().useTimings();
Map&lt;Class&lt;? extends Event&gt;, Set&lt;RegisteredListener&gt;&gt; ret = new HashMap&lt;Class&lt;? extends Event&gt;, Set&lt;RegisteredListener&gt;&gt;();
Set&lt;Method&gt; methods;
try {
Method[] publicMethods = listener.getClass().getMethods();
Method[] privateMethods = listener.getClass().getDeclaredMethods();
methods = new HashSet&lt;Method&gt;(publicMethods.length + privateMethods.length, 1.0f);
for (Method method : publicMethods) {
methods.add(method);
}
for (Method method : privateMethods) {
methods.add(method);
}
} catch (NoClassDefFoundError e) {
plugin.getLogger().severe(&quot;Plugin &quot; + plugin.getDescription().getFullName() + &quot; has failed to register events for &quot; + listener.getClass() + &quot; because &quot; + e.getMessage() + &quot; does not exist.&quot;);
return ret;
}
for (final Method method : methods) {
final EventHandler eh = method.getAnnotation(EventHandler.class);
if (eh == null) continue;
// Do not register bridge or synthetic methods to avoid event duplication
// Fixes SPIGOT-893
if (method.isBridge() || method.isSynthetic()) {
continue;
}
final Class&lt;?&gt; checkClass;
if (method.getParameterTypes().length != 1 || !Event.class.isAssignableFrom(checkClass = method.getParameterTypes()[0])) {
plugin.getLogger().severe(plugin.getDescription().getFullName() + &quot; attempted to register an invalid EventHandler method signature \&quot;&quot; + method.toGenericString() + &quot;\&quot; in &quot; + listener.getClass());
continue;
}
final Class&lt;? extends Event&gt; eventClass = checkClass.asSubclass(Event.class);
method.setAccessible(true);
Set&lt;RegisteredListener&gt; eventSet = ret.get(eventClass);
if (eventSet == null) {
eventSet = new HashSet&lt;RegisteredListener&gt;();
ret.put(eventClass, eventSet);
}
for (Class&lt;?&gt; clazz = eventClass; Event.class.isAssignableFrom(clazz); clazz = clazz.getSuperclass()) {
// This loop checks for extending deprecated events
if (clazz.getAnnotation(Deprecated.class) != null) {
Warning warning = clazz.getAnnotation(Warning.class);
WarningState warningState = server.getWarningState();
if (!warningState.printFor(warning)) {
break;
}
plugin.getLogger().log(
Level.WARNING,
String.format(
&quot;\&quot;%s\&quot; has registered a listener for %s on method \&quot;%s\&quot;, but the event is Deprecated. \&quot;%s\&quot;; please notify the authors %s.&quot;,
plugin.getDescription().getFullName(),
clazz.getName(),
method.toGenericString(),
(warning != null &amp;&amp; warning.reason().length() != 0) ? warning.reason() : &quot;Server performance will be affected&quot;,
Arrays.toString(plugin.getDescription().getAuthors().toArray())),
warningState == WarningState.ON ? new AuthorNagException(null) : null);
break;
}
}
final CustomTimingsHandler timings = new CustomTimingsHandler(&quot;Plugin: &quot; + plugin.getDescription().getFullName() + &quot; Event: &quot; + listener.getClass().getName() + &quot;::&quot; + method.getName() + &quot;(&quot; + eventClass.getSimpleName() + &quot;)&quot;, pluginParentTimer); // Spigot
EventExecutor executor = new EventExecutor() {
@Override
public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
try {
if (!eventClass.isAssignableFrom(event.getClass())) {
return;
}
// Spigot start
boolean isAsync = event.isAsynchronous();
if (!isAsync) timings.startTiming();
method.invoke(listener, event);
if (!isAsync) timings.stopTiming();
// Spigot end
} catch (InvocationTargetException ex) {
throw new EventException(ex.getCause());
} catch (Throwable t) {
throw new EventException(t);
}
}
};
if (false) { // Spigot - RL handles useTimings check now
eventSet.add(new TimedRegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled()));
} else {
eventSet.add(new RegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled()));
}
}
return ret;
}

Specifically

method.getParameterTypes()[0]

Does not work with generics.

答案1

得分: 1

你可以创建一个通用方法,返回BlockEventListenerObject,类似这样:

private static <T extends BlockEvent> BlockEventListener<T> getBlockEventListener(Class<T> clazz) {
    return new BlockEventListener<T>();
}

然后你可以在循环中调用它:

getServer().getPluginManager().registerEvents(getBlockEventListener(clazz), this);
英文:

You can create a generic method which returns you the BlockEventListenerObject, something like this:

private static &lt;T extends BlockEvent&gt; BlockEventListener&lt;T&gt; getBlockEventListener(Class&lt;T&gt; clazz) {
return new BlockEventListener&lt;T&gt;();
}

Then you can call it from your loop,

getServer().getPluginManager().registerEvents(getBlockEventListener(clazz), this);

答案2

得分: 0

> „&hellip;基本上我该如何使得类似这样的功能起作用&hellip;

这里有一种方法&hellip;

...
public void use( final Set&lt; Class &lt; ? extends BlockEvent &gt; &gt; classes, PluginManager pluginMgr ) throws ReflectiveOperationException { 
for( Class &lt; ? extends BlockEvent &gt; clazz : ( classes ) ){ 
pluginMgr.registerEvents( new BlockListener&lt; &gt;( clazz.newInstance( ) ), this );
}        
}
...

我已经实现了一些 临时替代 类来进行实验测试。我用以下方式使用了实验性的类&hellip;

...    
final Set&lt; Class &lt; ? extends BlockEvent &gt; &gt; classes = new HashSet&lt; &gt;( );
classes.add( BlockParty.class );
final PluginManager pluginMgr = new PluginManager( );
final Deduper experimental = new DeduperAnswer( );
experimental.use( classes, pluginMgr );
...

点击页面顶部的绿色开始按钮 并观察此输出&hellip;

BlockChangeEvent [ block: Block@404b9385 ]
哈瓦那街区派对!
实验成功
英文:

> „&hellip;Basically how could I get functionality like this to work&hellip;

Here is one way&hellip;

...
public void use( final Set&lt; Class &lt; ? extends BlockEvent &gt; &gt; classes, PluginManager pluginMgr ) throws ReflectiveOperationException { 
for( Class &lt; ? extends BlockEvent &gt; clazz : ( classes ) ){ 
pluginMgr.registerEvents( new BlockListener&lt; &gt;( clazz.newInstance( ) ), this );
}        
}
...

I've implemented some stand-in classes to make the experiment testable. I used the experimental classes like this&hellip;

...    
final Set&lt; Class &lt; ? extends BlockEvent &gt; &gt; classes = new HashSet&lt; &gt;( );
classes.add( BlockParty.class );
final PluginManager pluginMgr = new PluginManager( );
final Deduper experimental = new DeduperAnswer( );
experimental.use( classes, pluginMgr );
...

Click the green Start button at the top of the page and observe this output&hellip;

BlockChangeEvent [ block: Block@404b9385 ]
Havana Block Party!
EXPERIMENT SUCCESSFUL

huangapple
  • 本文由 发表于 2020年8月16日 23:53:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/63438940.html
匿名

发表评论

匿名网友

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

确定