2012-03-25 78 views
4

我知道类似的问题已经讨论了几个线程,但我仍然无法找到解决我的问题。Java泛型编译在eclipse中,而不是从命令行

其他线程: 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); 

    } 

新生成的编译时间错误也是日食。

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(项目 - >属性 - > Java构建路径 - >库)

在此先感谢。

更新:谢谢你们的回应。告诉我是否可以提供更多信息。我的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(); 
     } 
    } 
} 

错误,包括我运行命令:

[email protected]:~/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的一些详细信息。

[email protected]:~/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) 

[email protected]:~/mywork/GenericTest/src$ javac -version 
javac 1.6.0_30 
+1

为了更好地帮助越早,张贴[SSCCE](http://sscce.org/)。鉴于第一'?'是低于你的帖子提示我问..是你的问题“为什么SDK和Eclipse编译器之间的行为差​​异?”。如果是这样,那么我建议你添加,作为编辑,让你的问题..一个问题。 – 2012-03-25 11:38:43

+0

@Nitraj您确定在命令行中使用的JDK与Eclipse用于编译的相同吗?你可以在你的问题中添加你正在使用的命令行吗? – 2012-03-25 12:24:23

+0

为了重现您的编译错误,浏览5个代码片段中的不完整源代码传播非常困难。如果您希望我们帮助您,请发布SSCCE。提示:如果你使用默认可见性(即非公开)声明你的类型,它们都可以进入相同的源文件,这使得将代码导入eclipse变得更容易。 – meriton 2012-03-25 13:25:50

回答

0

我不认为我们有足够的细节来回答你的第一个问题。 但是,对于你的第二个问题,你可以使用喜好配置的Java运行时环境 - >爪哇 - >安装的JRE,你可以配置使用首你的Java编译器合规水平 - >爪哇 - >编译器

enter image description here

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。这就是为什么我不会推荐将类作为字符串传递的原因。

如果你真的需要他们的字符串,以获得花哨的类加载器,你至少应该检查类实现正确的类型:

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); 
} 

随着该实现,试图订阅StringEventHandler将导致ClassCastException

该代码还不够完善,如编译器警告提醒我们这样一个事实,我不检查事件处理程序有正确的类型参数,所以调用者仍然可以这样做:

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

即使尽管MyEventHandler无法处理所有Event s。但是,由于类型擦除,验证EventHandler的事件类型通常是不可能的。