java.lang.ClassCastException при создании прокси - PullRequest
0 голосов
/ 25 марта 2019

Моя цель - создать экземпляр из class, который реализует interface и расширяет еще один class.

... Аннотация объекта:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface Entity {

    String visibileName();

}

... * инвентарь 1009 *

      public class EventDesignDialog implements IEventDesignDialog{

        private String show;
        private String dateAndTimeDisplayFormat;
        private String eventType;


        @Entity(visibileName = "Show")
        public String getShow() {
            return this.show;
        }

        @Entity(visibileName = "Date And Time display format")
        public String getDateAndTimeDisplayFormat() {
            return this.dateAndTimeDisplayFormat;
        }

        @Entity(visibileName = "Event Type")
        public String getEventType() {
            System.out.println("get event type method invokde successfully");
            return this.eventType;
        }
}

IEventDesignDialog интерфейс:

public interface IEventDesignDialog extends IPage{

    public String getShow();

    public String getDateAndTimeDisplayFormat();

    public String getEventType();


}

IPage интерфейс:

public interface IPage {

}

Реализация динамического прокси:

public class IPageProxy implements InvocationHandler {
    private List<Method> entityMethods;



    private Class <? extends IPage> screenClazz;

    public IPageProxy(final Class <? extends IPage> screenClazz) {
        entityMethods = new ArrayList<>();
        getEntityAnnotatedMethods(screenClazz);
        // Accept the real implementation to be proxied
        this.screenClazz = screenClazz;
    }


    /**
     * create an page instance
     * @param type
     * @param
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public static IPage getInstance(final Class<? extends IPage> type)
            throws InstantiationException, IllegalAccessException {

        List<Class<?>> interfaces = new ArrayList<>();
        interfaces.addAll(Arrays.asList(type.getInterfaces()));

        return (IPage) Proxy.newProxyInstance(
                type.getClassLoader(),
                findInterfaces(type),
                new IPageProxy(type)
             );

        /*return (IPage) Proxy.newProxyInstance(type.getClassLoader(),
               interfaces.toArray(new Class<?>[interfaces.size()])
                , new IPageProxy(type));*/
    }


    /**
     * get all methods that annotated with @Entity annotation
     * and add it for entityMethods array List
     * @param screenClazz
     */
    private void getEntityAnnotatedMethods(final Class <? extends IPage>  screenClazz) {
        // Scan each interface method for the specific annotation
        // and save each compatible method
        for (final Method m : screenClazz.getDeclaredMethods()) {
            if (m.isAnnotationPresent(Entity.class)) {
                entityMethods.add(m);
            }
        }
    }


    static Class<?>[] findInterfaces(final Class<? extends IPage> type) {
        Class<?> current = type;

        do {
            final Class<?>[] interfaces = current.getInterfaces();

            if (interfaces.length != 0) {
                return interfaces;
            }
        } while ((current = current.getSuperclass()) != Object.class);

        throw new UnsupportedOperationException("The type does not implement any interface");
    }



    @Override
    public Object invoke(
            final Object proxy,
            final Method method,
            final Object[] args) throws InvocationTargetException, IllegalAccessException {
        // A method on MyInterface has been called!
        // Check if we need to go call it directly or if we need to
        // execute something else before!


        if (entityMethods.contains(method)) {
            // The method exist in our to-be-proxied list
            // Execute something and the call it
            // ... some other things
            System.out.println("Something else");
        }

        // Invoke original method
        return method.invoke(screenClazz, args);
    }

}

Основной класс:

public class Main {

    public static void main(String[] args) {
        try {

            ((EventDesignDialog)getInstance(EventDesignDialog.class)).getEventType();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }


    @SuppressWarnings("unchecked")
    public static <T extends IPage> T getInstance(final Class<? extends IPage> type) throws InstantiationException, IllegalAccessException {
        return (T) IPageProxy.getInstance(type);
    }

}

Выдается следующее исключение:

  Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy2 cannot be cast to abc.EventDesignDialog
    at abc.Main.main(Main.java:8)

1 Ответ

2 голосов
/ 25 марта 2019

Вы расширяете Screen, что означает, что это не interface.
Динамические прокси работают, только если в иерархии присутствует база interface.

interfaces.size() == 0

Таким образом, прокси не может реализовать никакой interface, и, очевидно, он не является частью иерархии Screen.


Если Screen был interfaceВаш метод все еще слишком сложен.Этого

public static Screen getInstance(Class<? extends Screen> type)

достаточно.


Вы по-прежнему получаете исключение, потому что

Class#getInterfaces

возвращает interface s, которые реализованы this class.

Это означает, что если вы вызовете его на EventDesignDialog.class, он вернет пустой массив.
Это означает, что если вы вызовете его на EntityDesignDialog.class, все равно он вернет пустоймассив.
Когда вы вызываете его на Screen.class, он вернет

[IPage.class]

. Вам нужно зациклить иерархию с

Class#getSuperclass

, пока не найдете подходящий interface.

Возможная реализация может выглядеть как

static Class<?>[] findInterfaces(final Class<?> type) {
    Class<?> current = type;

    do {
        final Class<?>[] interfaces = current.getInterfaces();

        if (interfaces.length != 0) {
            return interfaces;
        }
    } while ((current = current.getSuperclass()) != Object.class);

    throw new UnsupportedOperationException("The type does not implement any interface");
}

, что означает, что вам нужно изменить код на

return (IPage) Proxy.newProxyInstance(
              type.getClassLoader(),
              findInterfaces(type),
              new IPageProxy(type)
           );

Но, поскольку вы уже знаете, результат будетIPage прокси, вы можете просто

return (IPage) Proxy.newProxyInstance(
              type.getClassLoader(),
              new Class[] { IPage.class },
              new IPageProxy(type)
           );

Здесь

public static IPage getInstance(final Class<? extends IPage> type)

вы возвращаете IPage, но здесь

((EventDesignDialog)getInstance(EventDesignDialog.class))

вы пытаетесь понизить это, что означает, что вы пытаетесь привести его к более конкретному типу.Это невозможно, поскольку прокси не относится к типу EventDesignDialog, но просто реализует ваш интерфейс IPage.

Поскольку динамические прокси основаны на интерфейсе, вы будете вынуждены иметь делос интерфейсами.
Попытка привести к конкретным классам всегда вызовет исключение.

Если вам нужен IEventDesignDialog, вам нужен новый прокси специально для него.

...