java-генерики компилируются в eclipse, а не из командной строки - PullRequest
4 голосов
/ 25 марта 2012

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

Другие потоки: Компиляторы ведут себя по-разному с нулевым параметром универсальногометод

Общий код Java компилируется в eclipse, но не в командной строке

Поэтому я писал механизм событий, используя обобщенные значения для интерфейсов событий и обработчиков событий,У меня был следующий код для регистрации событий, который генерирует ошибку в командной строке javac, но не с eclipse indigo.В приведенном ниже коде EventRegistry просто захватывает имя класса события и обработчик события в строковом формате.

Я создал следующие интерфейсы

public interface Event{}
public interface EventHandler<T extends Event> {}

Методы в моем классе EventManager

public <T extends Event, P extends EventHandler<T>> void registerManagedHandler( EventRegistry er, ClassLoader cl ) throws ClassNotFoundException
    {
              ...

              Class<T> eventClass = (Class<T>) cl.loadClass(er.getEventClass());
              Class<P> eventHandlerClass = (Class<P>) cl.loadClass(er.getEventHandlerClass());

        ...
        register(eventHandlerClass, eventClass);
    }

private <T extends Event, P extends EventHandler<T>> void register( Class<P> handler, Class<T> eventClass )
    {
        ...
    }

... в каком-то другом классе.У меня есть утверждение.

EventManager.getInstance().registerManagedHandler(er,al);

Eclipse правильно компилирует код, но ошибка, сгенерированная при компиляции из javac в командной строке

incompatible types; inferred type argument(s) com.mycompany.events.Event,java.lang.Object do not conform to bounds of type variable(s) T,P
    [javac] found   : <T,P>void
    [javac] required: void
   [javac]                             EventManager.getInstance().registerManagedHandler(er,al);

Существуют аналогичные методы для unregistering/enabling/disabling событий, которыегенерирует ту же ошибку.Из приведенного выше кода я попытался удалить ограничения типа ( <T extends Event, P extends EventHandler<T>> ) из registerManagedHandler, но затем сгенерированный при вызове метода (...) вызов

мой измененный зарегистрированныйManagedHandler () ..

public void registerManagedHandler( EventRegistry er, ClassLoader cl ) throws ClassNotFoundException
    {
                        ...
            Class<? extends Event> eventClass = (Class<? extends Event>) cl.loadClass(er.getEventClass());
            Class<? extends EventHandler<? extends Event>> eventHandlerClass = (Class<? extends EventHandler<? extends Event>>) cl.loadClass(er.getEventHandlerClass());
             ...
        register(eventHandlerClass, eventClass);

    }

НовыйСгенерированное время компиляции Ошибка в eclipse.

Bound mismatch: The generic method register(Class<P>, Class<T>) of type EventManager is not applicable for the arguments 
 (Class<capture#20-of ? extends EventHandler<? extends Event>>, Class<capture#22-of ? extends Event>). The inferred type 
 capture#20-of ? extends EventHandler<? extends Event> is not a valid substitute for the bounded parameter <P extends 
 EventHandler<T>>

Я не собираюсь удалять проверки типов из метода register (...).

Пожалуйста, сообщите мне, если более подробно о коденеобходимы для понимания.

Пожалуйста, скажите мне правильный способ решения таких проблем или обходных путей.Я довольно новичок в дженериках, но я прочитал основные руководства, доступные до его реализации.

Также я не смог найти способ заставить eclipse использовать sun-javac6, установленный в моей системе Ubuntu вместо ее собственного компилятора.Хотя я знаю, как изменить JRE в Eclipse (Project -> Properties -> Java Build Path -> Libraries)

Заранее спасибо.

Обновление: Спасибо, ребята, за ваши ответы.Подскажите, могу ли я предоставить больше информации.Моя версия Eclipse - Indigo (3.7)

Вот sscce.Если вы запустите программу в Eclipse, она будет работать нормально (компиляция и выполнение).Но когда вы запускаете его с командной строкой: т.е. javac GenericTester.java, то появляется следующая ошибка.Я подтвердил это с помощью инструмента компилятора sscce.org.

interface Event {
}

interface EventHandler<T extends Event> {
}

class EventRegistry {
    public String eventClass;
    public String eventHandlerClass;
}

class EventManager {
    public <T extends Event, P extends EventHandler<T>> void registerEvent(
            Class<T> eventClass, Class<P> eventHandlerClass) {
    }

    public <T extends Event, P extends EventHandler<T>> void registerEvent(
            EventRegistry er) throws ClassNotFoundException {
        Class<T> eventClass = (Class<T>) this.getClass()
                .getClassLoader().loadClass(er.eventClass);
        Class<P> eventHandlerClass = (Class<P>) this
                .getClass().getClassLoader()
                .loadClass(er.eventHandlerClass);
        registerEvent(eventClass, eventHandlerClass);
    }
}

class MyEvent implements Event {
}

class MyEventHandler implements EventHandler<MyEvent> {
}

public class GenericTester {
    public static void main(String[] args) {
        EventRegistry er = new EventRegistry();
        er.eventClass = MyEvent.class.getName();
        er.eventHandlerClass = MyEventHandler.class.getName();
        try {
            new EventManager().registerEvent(er);
            System.out.println("It worked.");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

Ошибка в командной строке, включая команду, которую я запускаю:

nitiraj@pandora:~/mywork/GenericTest/src$ javac GenericTester.java 

GenericTester.java:40: incompatible types; inferred type argument(s) Event,java.lang.Object do not conform to bounds of type variable(s) T,P
found   : <T,P>void
required: void
            new EventManager().registerEvent(er);
                                            ^
Note: GenericTester.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error

Еще немного информации о Java у меня естьустановлен.

nitiraj@pandora:~/mywork/GenericTest/src$ java -version
java version "1.6.0_30"
Java(TM) SE Runtime Environment (build 1.6.0_30-b12)
Java HotSpot(TM) 64-Bit Server VM (build 20.5-b03, mixed mode)

nitiraj@pandora:~/mywork/GenericTest/src$ javac -version
javac 1.6.0_30

Ответы [ 3 ]

2 голосов
/ 22 января 2014

Это ошибка в Java 5 и Java 6, исправленная в Java 7

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6369605

0 голосов
/ 26 марта 2012

Почему JDK 1.6 отказался компилировать, что SSCCE объясняется по адресу: Компиляторы ведут себя по-разному с нулевым параметром универсального метода

В Java 7 алгоритм вывода для переменных типов, которые не отображаются в типах параметров или возвращаемых значениях, был расширенный , чтобы принять во внимание границы параметров типа. Действительно, ваш SSCCE успешно компилируется с использованием javac из JDK 1.7.0_02.

Тем не менее, я думаю, что ваш registerEvent неправильно использует дженерики: параметры типа метода определяются вызывающей стороной (обычно алгоритмом определения типа, но они могут быть указаны только явно), но ваш код является корректным только в том случае, если фактические аргументы типа вызывающей стороны совпадают с аргументами EventRegistry, которые не проверяются компилятором. Рассмотрим:

    er.eventHandlerClass = "java.lang.String";
    new EventManager().<MyEvent, MyEventHandler>registerEvent(er);

Это компилируется и работает нормально, но, вероятно, вызовет исключение ClassCastException, когда событие действительно запускается. Вот почему я бы не рекомендовал передавать классы как строки.

Если вам действительно нужно, чтобы они были строками, чтобы выглядело классно с ClassLoader, вы должны по крайней мере проверить, что классы реализуют правильные типы:

public void registerEvent(EventRegistry er) throws ClassNotFoundException {
    Class<? extends Event> eventClass = this.getClass()
            .getClassLoader().loadClass(er.eventClass)
            .asSubclass(Event.class);
    Class<? extends EventHandler> eventHandlerClass = this
            .getClass().getClassLoader()
            .loadClass(er.eventHandlerClass)
            .asSubclass(EventHandler.class);
    registerEvent(eventClass, eventHandlerClass);
}

При такой реализации попытка подписаться на String как EventHandler приведет к ClassCastException.

Этот код все еще не совершенен, так как предупреждение компилятора предупреждает нас о том, что я не проверяю, что EventHandler имеет правильный параметр типа, так что вызывающая сторона все еще может делать:

    er.eventClass = Event.class.getName();
    er.eventHandlerClass = MyEventHandler.class.getName();
    new EventManager().registerEvent(er);

, хотя MyEventHandler не может справиться со всеми Event с. Однако проверка типа события EventHandler в общем случае невозможна из-за стирания типа.

0 голосов
/ 25 марта 2012

Не думаю, что у нас достаточно деталей, чтобы ответить на ваш первый вопрос. Однако для второго вопроса вы можете настроить среды выполнения Java с помощью Предпочтения -> Java -> Установленные JRE , а уровень совместимости вашего Java-компилятора можно настроить с помощью Предпочтения -> Java -> Компилятор

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...