java泛型在eclipse中编译而不是从命令行编译

时间:2012-03-25 11:30:14

标签: java eclipse generics compilation

我知道已经在以下几个线程中讨论了类似的问题,但我仍然无法找到解决问题的方法。

其他主题: Compilers behave differently with a null parameter of a generic method

Java generics code compiles in eclipse but not in command line

所以我正在编写一个使用泛型的事件和事件处理程序接口的事件机制。我有以下代码用于注册在命令行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生成相同错误的事件有类似的方法。 从上面的代码我尝试从registerManagedHandler中删除类型约束( <T extends Event, P extends EventHandler<T>> ),但然后注册(...)方法调用生成错误

我修改的registeredManagedHandler()..

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使用我的Ubuntu系统上安装的sun-javac6而不是自己的编译器。虽然我知道如何在eclipse中更改JRE(Project - &gt; Properties - &gt; Java Build Path - &gt; 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 个答案:

答案 0 :(得分:2)

这是java 5和java 6中的一个错误,已在java 7中修复

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

答案 1 :(得分:0)

我认为我们没有足够的细节来回答您的第一个问题。 但是,对于第二个问题,您可以使用首选项 - &gt;配置Java运行时环境。 Java - &gt;已安装的JRE ,您可以使用首选项 - &gt;配置Java编译器合规性级别。 Java - &gt;编译器

enter image description here

答案 2 :(得分:0)

为什么JDK 1.6拒绝编译SSCCE的解释如下:Compilers behave differently with a null parameter of a generic method

在Java 7中,未出现在参数类型或返回值中的类型变量的推理算法是extended,以考虑类型参数的边界。实际上,您的SSCCE已成功编译JDK 1.7.0_02中的javac。

那就是说,我认为你的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 s。但是,由于类型擦除,通常无法验证EventHandler的事件类型。