Java中的动态类型转换
我正在为Minecraft服务器实现CraftBukkit编写一个插件,我遇到了一个问题,我需要转换到一个通过反射找到的类 事情是这样的。我编写的原始代码如下所示,删除了不相关的部分:Java中的动态类型转换,java,reflection,casting,bukkit,Java,Reflection,Casting,Bukkit,我正在为Minecraft服务器实现CraftBukkit编写一个插件,我遇到了一个问题,我需要转换到一个通过反射找到的类 事情是这样的。我编写的原始代码如下所示,删除了不相关的部分: import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Random; import net.minecraft.ser
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import net.minecraft.server.v1_7_R3.EntityAnimal;
import net.minecraft.server.v1_7_R3.EntityHuman;
import org.bukkit.craftbukkit.v1_7_R3.entity.CraftAnimals;
import org.bukkit.craftbukkit.v1_7_R3.entity.CrafteEntity;
import org.bukkit.World;
import org.bukkit.entity.Animals;
import org.bukkit.entity.Entity;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
public class Task extends BukkitRunnable {
private static final int MATING_DISTANCE = 14;
private final JavaPlugin plugin;
private final Random randomizer;
private boolean mateMode;
private double chance;
public Task(JavaPlugin plugin, double chance, boolean mateMode) {
this.plugin = plugin;
this.randomizer = new Random();
this.chance = chance;
this.mateMode = mateMode;
this.theTaskListener = listener;
}
public void run() {
List<World> worlds = plugin.getServer().getWorlds();
Iterator<World> worldIterator = worlds.iterator();
while (worldIterator.hasNext()) {
World world = worldIterator.next();
Collection<Animals> animals = world.getEntitiesByClass(Animals.class);
Iterator<Animals> animalIterator = animals.iterator();
while (animalIterator.hasNext()) {
Animals animal = (Animals) animalIterator.next();
EntityAnimal entity = (EntityAnimal) ((CraftEntity) ((CraftAnimals) animal)).getHandle();
EntityHuman feeder = null;
entity.f(feeder);
}
}
}
}
现在,我如何重写Task.run()方法,以便它使用反射类?这涉及到大量的类型转换,不幸的是,这一切都是必要的,因为Minecraft代码中有大量的重载。例如,entity.f(EntityHuman)方法不能简单地通过entity.f(null)调用,因为还有其他重载的entity.f(Object)方法
我愿意接受所有的建议,因为我在这里面临着死胡同。如果有更好的方法解决这个问题,我也可以改变
谢谢大家! 只是一个头脑风暴的想法。如果:
- 导入所有受支持的版本
- 完全引用适当包的类型
- 检查针对特定运行时的版本(假定可以以某种方式获得)
当然,这有点难看,但在特定的情况下,我首先想到的是这种可能性。在面向对象的语言中,我们可以访问各种设计模式,这些模式正是为此目的而开发的。我们特别使用两种模式 用于为许多不同的实现提供相同的接口。它有时被称为垫片。您可以为每个服务器的每个版本创建一个类,并将库导入到每个服务器。该类实现了它们共同持有的接口 用于在适配器类中进行选择。您可以使用任何需要的方法来确定您拥有的服务器版本,它将创建一个实现适当接口的对象。主代码保持不变。它调用工厂以获取知道如何处理服务器的对象
这种方法的优点有几个。导入重叠的库不会污染名称空间。随着新服务器版本的增加,主代码更不容易被更改;唯一需要编写的代码是新的服务器垫片和确定要生产哪个适配器的工厂。阅读Gerold Broser的响应后,我意识到我必须以某种方式修改我的方法,以便创建某种处理程序类来执行特定于版本的操作——当然,这将是一个接口,每个版本由一个类单独实现 然而,当我意识到Maven不允许我调用同一个groupid.artifactid对象的两个版本时,这就成了一个问题 我很快做了一些研究,发现了mbaxter的以及完美地展示了这种方法的实现
这种方法非常有效,每个Bukkit开发人员都应该使用这是我完成的插件,如有必要,可供进一步参考。-1对Minecraft服务器实现的指责是故意忽略Java的约定。更糟糕的是,因为版本污染了它。你知道这样做的原因吗?我知道,这对你没有特别的帮助。将其视为道义上的支持(此外,我当然想成为第一个提到这一点的人;-)。然而,我要看看我是否能找到一个更有用的答案。我认为这样做是因为两个不同版本中的类往往不同,因此导致插件在不同版本中失败——这迫使开发人员出于各种原因审查他们的插件。但这绝对不是最好的方法。
private static final String[] requiredClasses = {
"net.minecraft.server.%s.EntityAnimal",
"net.minecraft.server.%s.EntityHuman",
"org.bukkit.craftbukkit.%s.entity.CraftAnimals",
"org.bukkit.craftbukkit.%s.entity.CraftEntity"
};
public static final String[] supportedVersions = {
"v1_7_R3",
"v1_7_R4"
};
public Class<?>[] initializeClasses() {
String correctVersion = null;
for (int i = 0; i < supportedVersions.length; i++) {
String version = supportedVersions[i];
boolean hadIssues = false;
for (int j = 0; j < requiredClasses.length; j++) {
String className = requiredClasses[j];
try {
Class.forName(String.format(className, version));
} catch (ClassNotFoundException e) {
getLogger().log(Level.INFO, String.format("The correct version isn't %s.", version));
hadIssues = true;
break;
}
}
if (!hadIssues) {
correctVersion = version;
break;
}
}
Class[] classes = new Class[requiredClasses.length];
if (correctVersion != null) {
getLogger().log(Level.INFO, String.format("The correct version is %s.", correctVersion));
for (int i = 0; i < requiredClasses.length; i++) {
String className = requiredClasses[i];
try {
classes[i] = Class.forName(String.format(className, correctVersion));
} catch (ClassNotFoundException e) {}
}
} else {
getLogger().log(Level.WARNING, "The version of Minecraft on this server is not supported.");
getLogger().log(Level.WARNING, "Due to this, the plugin will self-disable.");
getLogger().log(Level.WARNING, "To fix this issue, get build that supports your version.");
this.setEnabled(false);
}
return classes;
}
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import org.bukkit.World;
import org.bukkit.entity.Animals;
import org.bukkit.entity.Entity;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
public class Task extends BukkitRunnable {
private static final int MATING_DISTANCE = 14;
private final JavaPlugin plugin;
private final Random randomizer;
private boolean mateMode;
private double chance;
private Class entityAnimal;
private Class entityHuman;
private Class craftAnimals;
private Class craftEntity;
public Task(JavaPlugin plugin, Class[] classes, double chance, boolean mateMode) {
this.plugin = plugin;
this.randomizer = new Random();
this.chance = chance;
this.mateMode = mateMode;
this.entityAnimal = classes[0];
this.entityHuman = classes[1];
this.craftAnimals = classes[2];
this.craftEntity = classes[3];
}
import net.minecraft.server.v1_7_R3.*;
import net.minecraft.server.v1_7_R4.*;
enum Version {
V1_7_R3,
V1_7_R4
}
Version currentVersion;
net.minecraft.server.v1_7_R3.EntityAnimal animal3;
net.minecraft.server.v1_7_R4.EntityAnimal animal4;
// obtain currentVersion
switch ( currentVersion ) {
case V1_7_R3:
animal3.method();
break;
case V1_7_R4:
animal4.method();
break;
}