【问题标题】:ClassCastException for no reason?ClassCastException 无缘无故?
【发布时间】:2016-04-30 12:50:37
【问题描述】:

我似乎无缘无故地收到了 ClassCastException。让我解释一下我的代码结构: AbilityModifier 是一个抽象类 ProjectileModifier 是一个抽象类,它扩展了 AbilityModifier NoGravity 是一个扩展 ProjectileModifier 的类

AbilityModifier 有一个自定义 ClassLoader,我用它来查找所有能力修饰符。这样我就没有手动注册它们了。但是,要自动注册它们,我需要转换它们并将它们添加到列表中。当我施放它时,它会抛出一个 ClassCastException,说 redempt.divinity.ability.modifier.modifiers.NoGravity 不能施放为 redempt.divinity.ability。我做了一个测试,一切正常,没有例外:

package redempt.test;

public class Main {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Class<?> clazz = C.class;
        Object o = clazz.newInstance();
        A a = A.class.cast(o);
        System.out.println("Finished!");
    }
}
class A {
    public A() {

    }
}
class B extends A {
    public B() {

    }
}
class C extends B {
    public C() {

    }
}

所以这行得通,但由于某种原因,我的其他代码没有。让我给你所有涉及的课程:

能力修饰符:

package redempt.divinity.ability.modifier;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import redempt.divinity.Main;
import redempt.divinity.ability.AbilityType;

public abstract class AbilityModifier {
    public static Set<AbilityModifier> modifiers = new HashSet<AbilityModifier>();
    public abstract void onUse(Player player);
    public abstract String getName();
    public abstract Material getRepresentation();
    public abstract AbilityType getType();
    public static void registerAll() throws IOException, ClassNotFoundException, URISyntaxException, InstantiationException, IllegalAccessException {
        File file = new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI());
        JarFile jar = new JarFile(file);
        file.toURI().toURL();
        URL[] urls = {file.toURI().toURL()};
        List<String> names = new ArrayList<String>();
        URLClassLoader loader = URLClassLoader.newInstance(urls, ClassLoader.getSystemClassLoader());
        Enumeration<JarEntry> entries = jar.entries();
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            String name = entry.getName().replace("/", ".");
            if (name.endsWith(".class")) {
                try {
                    Class<?> clazz = loader.loadClass(name.replaceAll("\\.class$", ""));
                    System.out.println("Loading class: " + clazz.getSimpleName());
                    System.out.println(isSubclass(clazz));
                    if (isSubclass(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {
                        names.add(clazz.getSimpleName());
                        AbilityModifier modifier = AbilityModifier.class.cast(clazz.newInstance());
                    }
                } catch (NoClassDefFoundError e) {
                    System.out.println("Unable to load class: " + name);
                }
            }
        }
        loader.close();
        jar.close();
        System.out.println(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI().toURL().getPath());
        System.out.println("All ability modifiers found:");
        for (String name : names) {
            System.out.println(name);
        }
    }
    public static void register(Class<? extends AbilityModifier> clazz) {

    }
    private static boolean isSubclass(Class<?> clazz) {
        if (clazz.getSuperclass().equals(Object.class)) {
            return false;
        }
        return clazz.getSuperclass().getName().equals(AbilityModifier.class.getName()) ? true : isSubclass(clazz.getSuperclass());
    }
}

ProjectileModifier:

package redempt.divinity.ability.modifier;

import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import redempt.divinity.ability.Ability;

public abstract class ProjectileModifier extends AbilityModifier {
    public abstract void onUse(Player player, Entity projectile);
    @Override
    public void onUse(Player player) {
    }
    public abstract void onHit(Ability item, int level, EntityDamageByEntityEvent event);
    public abstract void onTick(Entity projectile);
}

无重力:

package redempt.divinity.ability.modifier.modifiers;

import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.util.Vector;
import redempt.divinity.ability.Ability;
import redempt.divinity.ability.AbilityType;
import redempt.divinity.ability.modifier.ProjectileModifier;

public class NoGravity extends ProjectileModifier {
    Vector vector;
    Location lastpos;
    @Override
    public void onUse(Player player, Entity projectile) {
        vector = player.getLocation().getDirection().normalize();
        lastpos = projectile.getLocation().clone();
    }
    @Override
    public String getName() {
        return "No gravity";
    }
    @Override
    public Material getRepresentation() {
        return Material.FEATHER;
    }
    @Override
    public AbilityType getType() {
        return AbilityType.PROJECTILE;
    }
    @Override
    public void onHit(Ability item, int level, EntityDamageByEntityEvent event) {
    }
    @Override
    public void onTick(Entity entity) {
        lastpos.add(vector);
        if (lastpos.getBlock().getType().equals(Material.AIR)) {
            entity.setVelocity(vector);
        }
        lastpos = entity.getLocation().clone();
    }
    public NoGravity() {
    }
}

例外:

java.lang.ClassCastException: redempt.divinity.ability.modifier.modifiers.NoGravity cannot be cast to redempt.divinity.ability.modifier.AbilityModifier
        at redempt.divinity.ability.modifier.AbilityModifier.registerAll(AbilityModifier.java:47) ~[?:?]
        at redempt.divinity.Main.onEnable(Main.java:30) ~[?:?]
        at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:292) ~[spigot-1.9.jar:git-Spigot-f04e043-52c7fc0]
        at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:340) [spigot-1.9.jar:git-Spigot-f04e043-52c7fc0]
        at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:405) [spigot-1.9.jar:git-Spigot-f04e043-52c7fc0]
        at org.bukkit.craftbukkit.v1_9_R1.CraftServer.loadPlugin(CraftServer.java:361) [spigot-1.9.jar:git-Spigot-f04e043-52c7fc0]
        at org.bukkit.craftbukkit.v1_9_R1.CraftServer.enablePlugins(CraftServer.java:321) [spigot-1.9.jar:git-Spigot-f04e043-52c7fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.t(MinecraftServer.java:411) [spigot-1.9.jar:git-Spigot-f04e043-52c7fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.l(MinecraftServer.java:376) [spigot-1.9.jar:git-Spigot-f04e043-52c7fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.a(MinecraftServer.java:331) [spigot-1.9.jar:git-Spigot-f04e043-52c7fc0]
        at net.minecraft.server.v1_9_R1.DedicatedServer.init(DedicatedServer.java:269) [spigot-1.9.jar:git-Spigot-f04e043-52c7fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.run(MinecraftServer.java:527) [spigot-1.9.jar:git-Spigot-f04e043-52c7fc0]
        at java.lang.Thread.run(Unknown Source) [?:1.8.0_65]

【问题讨论】:

  • 请发布异常的堆栈跟踪并在您的代码中指出它发生的位置。
  • 能否请您添加完整的异常消息?
  • 好的,请稍等
  • @Manish 你错了。可以将子类转换为(其中一个)超类,这只是(通常)不必要的。问题更可能是类加载器之一,并且使用的 AbilityModifier 类是由与 NoGravity 及其(大)父级 AbilityModifier 不同的类加载器加载的(因此AbilityModifier(!)有两个不同的类实例)。
  • @ViperLordX 这取决于加载了哪个类加载器AbilityModifier。使用加载 AbilityModifier 的类加载器(例如 AbilityModifier.class.getClassLoader())可能会更好。也就是说,我不经常使用类加载器,所以我可能有点偏离。

标签: java class casting


【解决方案1】:

问题在于 NoGravity 类是通过与您在代码中引用的 AbilityModifier 类不同的类加载器加载的。因此,NoGravity 有一个不同的类实例 AbilityModifier 作为其祖父级,因此无法进行强制转换。

要解决此问题,您需要使用与在代码中加载 AbilityModifier 的类加载器相同的类加载器。

你需要改变:

URLClassLoader loader = URLClassLoader.newInstance(urls,
    ClassLoader.getSystemClassLoader());

URLClassLoader loader = URLClassLoader.newInstance(urls,
    AbilityModifier.class.getClassLoader());

更多详情请见Understanding Java class loading:

在运行时,Java 类由一对唯一标识——类的完全限定名和加载它的定义加载器。如果相同的命名(即相同的完全限定名称)类由两个不同的加载器定义,则这些类是不同的——即使 .class 字节相同并且从相同的位置(URL)加载。

【讨论】:

    【解决方案2】:

    由于 C 是 A 的间接子类,在本例中为 AbilityModifier 的 NoGravity,因此 NoGravity 的每个实例都是 AbilityModifier 的一个实例。但是,clazz.newInstance() 方法返回一个必须强制转换的 Object。我建议您在将其转换为 NoGravity 后尝试为该对象分配一个 AbilityModifier,如下所示:

    AbilityModifier a = (NoGravity) clazz.newInstance();

    编辑,您可以尝试的另一件事是:

    if (AbilityModifier.class.isAssignableFrom(clazz)) {
        AbilityModifier instance = ((Class<AbilityModifier>) clazz).newInstance();
    }
    

    希望这会有所帮助!

    【讨论】:

    • 稍微偏离主题,据我所知,您可以废弃整个 isSubclass() 方法并将其替换为 AbilityModifier.class.isAssignableFrom(loadedClass);
    【解决方案3】:

    只需构建->清理您的项目。

    【讨论】:

    • 什么意思,清理我的项目?
    • 清理项目将如何帮助解决 ClassCastException?这不是 ClassNotFoundException,您可以预期项目中出现此类异常或缺少库。
    • 只需转到 android studio 工具栏中的构建选项并尝试清理然后重建然后运行您的项目
    猜你喜欢
    • 2017-04-11
    • 1970-01-01
    • 2023-04-03
    • 2018-06-14
    • 2021-02-09
    • 1970-01-01
    • 2011-07-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多