2015-07-25 2 views
6

По какой-то причине, попытка отличить класс X к другому классу, Y, в третьем классе Z выбрасывает ClassCastException. Это кажется мне неправильным, поскольку класс X расширяет класс Y. Есть ли какая-то конкретная причина, почему класс X не может быть отлит до Y, хотя X расширяет его?Невозможно отнести класс «X» к классу «Y», хотя X расширяет Y?

См следующий код для справки:

Y:

public abstract class Y { 
    /** 
     * Called when the extension is enabled. 
     */ 
    public void onEnable() { 
    } 
} 

X:

public class X extends Y { 
    @Override 
    public void onEnable() { 
     // Extension specific code. 
    } 
} 

Z: (Этот код является т он кода, где ClassCastException берет свое начало.)

public class Z { 
    private boolean loadExtension(ExtensionDescription description) { 
     try { 
      URLClassLoader loader = new ExtensionClassLoader(new URL[]{description.getFile().toURI().toURL()}); 
      Y y = (Y) loader.loadClass(description.getMain()).newInstance(); 
     } catch (Throwable t) {} 
    } 
} 

Если loader.loadClass(description.getMain()).newInstance(); известно, чтобы создать новый экземпляр X, то почему бы кастинг на Y дело ClassCastException?

+0

В программе, проверьте 'X.class == loader.loadClass (description.getMain() '. –

+3

Только причина будет заключаться в том, что X и Y загружаются двумя разными загрузчиками классов. – TheCodingFrog

+0

@ TheCodingFrog. На самом деле это может быть проблема что я думаю об этом. Класс Z работает в своей собственной программе, тогда как класс X запущен в программе, которая выполняется программой, в которой запущен класс Z. (Если это имеет смысл, это довольно сложно.) Мне, возможно, придется выяснить, как получить загрузчик классов родительской программы. –

ответ

4

Только для дополнительной иллюстрации, вот пример:

Создание настраиваемого ClassLoader, например, ниже (копируется из here)

package com.dd; 

import java.io.BufferedInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.util.HashMap; 
import java.util.Map; 

public class CustomClassLoader extends ClassLoader { 

    /** 
    * The HashMap where the classes will be cached 
    */ 
    private Map<String, Class<?>> classes = new HashMap<String, Class<?>>(); 

    @Override 
    public String toString() { 
     return CustomClassLoader.class.getName(); 
    } 

    @Override 
    protected Class<?> findClass(String name) throws ClassNotFoundException { 

     if (classes.containsKey(name)) { 
      return classes.get(name); 
     } 

     byte[] classData; 

     try { 
      classData = loadClassData(name); 
     } catch (IOException e) { 
      throw new ClassNotFoundException("Class [" + name 
        + "] could not be found", e); 
     } 

     Class<?> c = defineClass(name, classData, 0, classData.length); 
     resolveClass(c); 
     classes.put(name, c); 

     return c; 
    } 

    /** 
    * Load the class file into byte array 
    * 
    * @param name 
    *   The name of the class e.g. com.codeslices.test.TestClass} 
    * @return The class file as byte array 
    * @throws IOException 
    */ 
    private byte[] loadClassData(String name) throws IOException { 
     BufferedInputStream in = new BufferedInputStream(
       ClassLoader.getSystemResourceAsStream(name.replace(".", "/") 
         + ".class")); 
     ByteArrayOutputStream out = new ByteArrayOutputStream(); 
     int i; 

     while ((i = in.read()) != -1) { 
      out.write(i); 
     } 

     in.close(); 
     byte[] classData = out.toByteArray(); 
     out.close(); 

     return classData; 
    } 
} 

А вот класс Z

package com.dd; 

import java.lang.reflect.InvocationTargetException; 

public class Z { 

    public static void main(String[] args) throws ClassNotFoundException, 
      InstantiationException, IllegalAccessException, 
      NoSuchMethodException, SecurityException, IllegalArgumentException, 
      InvocationTargetException { 

     CustomClassLoader loader = new CustomClassLoader(); 
     Class<?> c1 = loader.findClass("com.dd.X"); 

     System.out.println("Classloader:: "+ X.class.getClassLoader()); 
     System.out.println("Classloader:: "+ loader.findClass("com.dd.X").getClassLoader()); 

     X x = (X)c1.newInstance(); 
    } 
} 

И вот результат:

Classloader:: [email protected] 
Classloader:: com.dd.CustomClassLoader 
Exception in thread "main" java.lang.ClassCastException: com.dd.X cannot be cast to com.dd.X 
    at com.dd.Z.main(Z.java:18) 
+0

Спасибо. Теперь я понимаю проблему. Однако я не понимаю, как можно исправить это? Не обойти тот факт, что здесь работают два разных загрузчика классов. Это связано с тем, что идея о том, что происходит здесь: одна программа имеет плагиновую систему ... она загружает плагин с плагиновой системой, а вторая система плагинов пытается загрузить плагин.Я предполагаю, что это столкновение между родительским загрузчиком классов и плагином Classloader. Итак, с этим выведенным, есть ли способ исправить это или мне нужно переосмыслить логику программы? –

+2

Перед тем, как найти класс, каждый загрузчик классов по умолчанию запрашивает его родительский элемент, поэтому вы должны установить родителя и убедиться, что из него загружаются все типы, которые вам нужно использовать в вашем приложении (особенно при произведении). – eckes

+0

@eckes OK. Благодаря! Я посмотрю, что я могу сделать, чтобы это произошло. –

Смежные вопросы