Java Version: 6
我已经构建了一个aotuloader,它从JAR中加载类并实例化它们。所以我得到一个数组列表的所有实例匹配到特定的路径和抽象类。
package com.geNAZt.RegionShop.Util;
import com.geNAZt.RegionShop.RegionShopPlugin;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class Loader {
public static <T> ArrayList<T> loadFromJAR(RegionShopPlugin plugin, String path, Class interf) {
ArrayList<T> returnObjects = new ArrayList<T>();
try {
String pathToJar = Loader.class.getProtectionDomain().getCodeSource().getLocation().getPath();
JarFile jarFile = new JarFile(pathToJar);
Enumeration e = jarFile.entries();
URL[] urls = { new URL("jar:file:" + pathToJar +"!/") };
ClassLoader cl = URLClassLoader.newInstance(urls);
while (e.hasMoreElements()) {
JarEntry je = (JarEntry) e.nextElement();
if(je.isDirectory() || !je.getName().endsWith(".class") || !je.getName().substring(0,je.getName().length()-6).replace("/", ".").contains(path +".")){
continue;
}
try {
String className = je.getName().substring(0,je.getName().length()-6);
className = className.replace('/', '.');
Class<?> c = cl.loadClass(className);
if(!c.getSuperclass().getName().equals(interf.getName())) {
continue;
}
Constructor[] cons = c.getDeclaredConstructors();
for(Constructor con : cons) {
try {
returnObjects.add((T)con.newInstance(plugin));
break;
} catch (InvocationTargetException e1) {
e1.printStackTrace();
continue;
} catch (InstantiationException e1) {
e1.printStackTrace();
continue;
} catch (IllegalAccessException e1) {
e1.printStackTrace();
continue;
} catch (IllegalArgumentException e1) {
e1.printStackTrace();
continue;
}
}
} catch(ClassNotFoundException er) {
er.printStackTrace();
continue;
}
}
} catch(IOException e) {
e.printStackTrace();
return null;
}
return returnObjects;
}
}
它工作良好,我得到正确的对象:
private ArrayList<ShopCommand> loadedCommands = new ArrayList<ShopCommand>();
loadedCommands = loadFromJAR(pl, "com.geNAZt.RegionShop.Command.Shop", ShopCommand.class);
如果我做一个日志,所有对象似乎都很好:
12:24:40 [INFO] [RegionShop] Loaded ShopCommands: [com.geNAZt.RegionShop.Command.Shop.]ShopAdd@3b39c41d com.geNAZt.RegionShop.Command.Shop。ShopBuy@4d7a6a4b, com.geNAZt.RegionShop.Command.Shop。ShopDetail@1fd889aa com.geNAZt.RegionShop.Command.Shop。ShopEquip@4136083b com.geNAZt.RegionShop.Command.Shop。ShopList@42567aef, com.geNAZt.RegionShop.Command.Shop.ShopName@3ba102ef]
但是我想要使用它们。为了使用它们,我想将它们转换到ShopCommand类中。但是我得到这个错误:
12:24:40 [SCHWERWIEGEND]启用RegionShop v1.1.0b3时发生错误(是最新的吗?)java.lang.ClassCastException: com.geNAZt.RegionShop.Command.Shop.ShopAdd不能转换为com.geNAZt.RegionShop.Command.ShopCommand在com.geNAZt.RegionShop.Command.ShopExecutor。(ShopExecutor.java: 40)com.geNAZt.RegionShop.RegionShopPlugin.onEnable (RegionShopPlugin.java: 80)org.bukkit.plugin.java.JavaPlugin.setEnabled (JavaPlugin.java: 217)org.bukkit.plugin.java.JavaPluginLoader.enablePlugin (JavaPluginLoader.java: 457)org.bukkit.plugin.SimplePluginManager.enablePlugin (SimplePluginManager.java: 383)org.bukkit.craftbukkit.v1_5_R3.CraftServer.loadPlugin (CraftServer.java: 305)org.bukkit.craftbukkit.v1_5_R3.CraftServer.enablePlugins (CraftServer.java: 287)net.minecraft.server.v1_5_R3.MinecraftServer.j (MinecraftServer.java: 310)net.minecraft.server.v1_5_R3.MinecraftServer.e (MinecraftServer.java: 289)net.minecraft.server.v1_5_R3.MinecraftServer.a (MinecraftServer.java: 249)net.minecraft.server.v1_5_R3.DedicatedServer.init (DedicatedServer.java: 152)net.minecraft.server.v1_5_R3.MinecraftServer.run (MinecraftServer.java: 388)net.minecraft.server.v1_5_R3.ThreadServerApplication.run(源文件:573)
是否有可能将对象转换为抽象类,以便从类中获得api ?或者是否有其他方法从类中获取API ?
这个异常是类身份危机的结果。不能在类装入器之间强制转换。如本网站所述:
在使用多个类时也可能出现其他类型的混淆加载程序。图2显示了一个类身份危机的例子当接口和关联的实现都是由两个独立的类装入器装入。即使名称和二进制接口和类的实现是相同的不能将来自一个装入器的类的实例识别为从其他加载器实现接口。
编辑
虽然解决方案是由OP发现的,但我想给出我的两分钱。如果将该类移动到系统类加载器的空间,则可以很容易地解决导致无法识别来自其他ClassLoader
的类实例的混淆。使System Class Loader
成为新生成的ClassLoader
s的母体。这将导致不同的ClassLoader
共享同一个类。这可以通过以下方式通过URLClassLoader(URL[] urls,ClassLoader parent)
实现:
URL[] urls = { new URL("jar:file:" + pathToJar +"!/") };
ClassLoader cl = new URLClassLoader(urls,ClassLoader.getSystemClassLoader());