我正在爲Minecraft服務器實現CraftBukkit編寫一個插件,並且遇到了需要轉換爲通過反射發現的類的問題。Java中的動態類型轉換
這是交易。我寫的原代碼看起來像這樣,不相關的部分刪除:
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);
}
}
}
}
然而,正如你可以在進口看,這個代碼只有一個版本的Minecraft服務器軟件包的進口類 - v1_7_R3。現在的問題是,我想爲此添加更多的支持,並且我希望能夠在不爲每個版本的Minecraft創建插件的單獨版本的情況下做到這一點。儘管包中的大多數類都是相同的(至少是我需要的所有類),但包名稱不同,因此無法使用靜態導入來完成(或者至少我認爲是這樣)?
所以,我決定爲了得到正確的類,我需要使用反射(此代碼是在另一個類):
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];
}
現在,我怎麼可以重寫Task.run()方法,它會使用反射類?涉及到大量的類型轉換,不幸的是,由於Minecraft代碼中的重載量過大,所有這一切都是必需的。例如,entity.f(EntityHuman human)方法不能簡單地通過執行entity.f(null)來調用,因爲還有其他重載entity.f(Object object)方法。
我接受所有建議,因爲我在這裏面臨死衚衕。如果問題有更好的解決方法,我也可以改變。
謝謝!
-1 to * Minecraft服務器實現Bukkit *,用於故意忽略Java的[命名包](http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html)公約。更糟的是,用這個版本污染它。你知道這樣做的原因(也許是好的)嗎?我知道,這並不能幫助你。把它看作是道義上的支持(另外,我當然想成爲最先提到這一點的人;-)。但是,我會看看我能否找出更有用的答案。 – 2014-09-20 10:12:57
我認爲這樣做是因爲兩個獨立版本中的類往往不同,因此導致插件在不同版本中失敗 - 這迫使開發人員爲各種原因檢查插件。這絕對不是這樣做的最好方式。 – funstein 2014-09-20 10:22:21