2011-02-24 31 views
1

我有以下工厂类。它有两个采用Class实例并返回相应对象的方法。它们具有相同的方法名称,并且这两种方法都以Class作为参数,但具有不同的泛型类,也返回不同的类型。编译器是否认为这两种方法是重复的?当我打开Eclipse的Java文件,它像报告错误:具有不同泛型的方法参数是否使方法具有不同的签名?

描述资源路径位置类型 方法lookupHome(类)具有相同的擦除lookupHome(类),如类型的另一种方法EJBHomeFactory EJBHomeFactory.java

但是,它似乎不会报告任何错误。

import javax.ejb.EJBHome; 
import javax.ejb.EJBLocalHome; 

public class EJBHomeFactory { 
    public <T extends EJBHome> T lookupHome(Class<T> homeClass) throws PayrollException { 
     return lookupRemoteHome(homeClass); 
    } 

    public <T extends EJBLocalHome> T lookupHome(Class<T> homeClass) throws PayrollException { 
     return lookupLocalHome(homeClass); 
    } 

    /* ... define other methods ... */ 
} 

更新1:这是使代码通过的蚂蚁脚本,我不知道它是如何工作的,但它不会引发任何错误。看起来编译器是Eclipse JDT编译器,我用常规的javac尝试过,并且它不能编译。

<target name="compile" depends="init" description="Compile Java classes"> 
    <property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter"/> 
    <mkdir dir="${build.classes.dir}"/> 
    <javac destdir="${build.classes.dir}" 
      srcdir="${build.src.dir};${devsrc.dir}" 
      deprecation="${build.deprecation}" 
      debug="${build.debug}" 
      source="${build.source}" 
      target="${build.target}" 
      nowarn="${suppress.warning}" 
      bootclasspath="${bootclasspath}" > 
     <classpath> 
      <path refid="build.class.path.id"/> 
     </classpath> 
    </javac> 
</target> 

更新2:我刚刚创建另一个例子,有两个基类,并有两个子类,工厂类需要类和基于参数的类型实例。代码不能由javac编译,而在Eclipse中,IDE抱怨同样的擦除问题。下面是代码: 两个空基类:

public class BaseClassFirst { 
} 

public class BaseClassSecond { 
} 

两个子类:

public class SubClassFirst extends BaseClassFirst { 
    private int someValue = 0; 

    public SubClassFirst() { 
     System.out.println(getClass().getName()); 
    } 

    public SubClassFirst(int someValue) { 
     this.someValue = someValue; 
     System.out.println(getClass().getName() + ": " + this.someValue); 
    } 
} 

public class SubClassSecond extends BaseClassSecond { 
    private int someValue = 0; 

    public SubClassSecond() { 
     System.out.println(getClass().getName()); 
    } 

    public SubClassSecond(int someValue) { 
     this.someValue = someValue; 
     System.out.println(getClass().getName() + ": " + this.someValue); 
    } 
} 

因子类: 进口java.lang.reflect.Method中;

public class ClassFactory { 

    private static ClassFactory instance = null; 

    private ClassFactory() { 
     System.out.println("Welcome to ClassFactory!"); 
    } 

    public static synchronized ClassFactory getInstance() { 
     if (instance == null) { 
      instance = new ClassFactory(); 
     } 
     return instance; 
    } 

    public <T extends BaseClassFirst> T createClass(Class<T> firstClazz) { 
     if (firstClazz.equals(SubClassFirst.class)) { 
      try { 
       return firstClazz.newInstance(); 
      } catch (InstantiationException e) { 
       e.printStackTrace(); 
      } catch (IllegalAccessException e) { 
       e.printStackTrace(); 
      } 
     } 
     return null; 
    } 

    public <T extends BaseClassSecond> T createClass(Class<T> secondClazz) { 
     if (secondClazz.equals(SubClassSecond.class)) { 
      try { 
       return secondClazz.newInstance(); 
      } catch (InstantiationException e) { 
       e.printStackTrace(); 
      } catch (IllegalAccessException e) { 
       e.printStackTrace(); 
      } 
     } 
     return null; 
    } 

    public static void main(String[] args) { 
     ClassFactory factory = ClassFactory.getInstance(); 
     SubClassFirst first = factory.createClass(SubClassFirst.class); 
     SubClassSecond second = factory.createClass(SubClassSecond.class); 
     for (Method method : ClassFactory.class.getDeclaredMethods()) { 
      System.out.println(method); 
     } 
    } 
} 

回答

3

由于返回类型是签名的一部分,因此可以在编译时解析该方法,因此可以使用该方法。擦除之后,你有两种方法

public EJBHome lookupHome(Class homeClass) throws PayrollException; 
public EJBLocalHome lookupHome(Class homeClass) throws PayrollException; 

没有泛型无法定义,但这些作为返回类型是签名的情况下,这些不同的方法。

您可以拨打

lookupHome(EJBHome.class); 
lookupHome(EJBLocalHome.class); 

但不

Class c= EJBHome.class; 
lookupHome(c); // Ambiguous method call. 

编辑:请尝试以下。

for (Method method : EJBHomeFactory.class.getDeclaredMethods()) { 
    System.out.println(method); 
} 

,你应该看到类似

public javax.ejb.EJBHome EJBHomeFactory.lookupHome(java.lang.Class) 
public javax.ejb.EJBLocalHome EJBHomeFactorylookupHome(java.lang.Class) 

同样,如果您使用的javap -c。

invokevirtual#8; //方法lookupHome:(Ljava/lang/Class;)Ljavax/ejb/EJBHome;

invokevirtual#10; //方法lookupHome:(Ljava/lang/Class;)Ljavax/ejb/EJBLocalHome;

编辑如果你想要一个返回类型是签名的一部分的例子..

class A { 
    public static Byte getNum() { return 0; } 
} 

class B { 
    public static void main(String ... args) { 
    int i = A.getNum(); 
    System.out.println(i); 
    } 
} 

编译和运行这个和你没有错误,现在改变getNum的签名A到

,并仅编译类A.如果返回类型是不是签名的组成部分,这将使到B没有什么区别,但是如果你经营的B无需重新编译它,你得到

Exception in thread "main" java.lang.NoSuchMethodError: A.getNum()Ljava/lang/Byte; 
at B.main(B.java:10) 

正如你所看到的,返回类型Ljava/lang/Byte;是返回类型的inetrnal名称和签名的一部分。

+0

彼得,谢谢你的回答。这是否意味着编译器是否引发错误取决于这两种方法的调用方式?或者在极端的情况下,如果这两种方法从未被调用过,编译器会在这两种方法上抛出错误? – Sapience 2011-02-24 19:22:03

+0

返回类型不是签名的一部分。 – jprete 2011-02-25 06:53:42

+0

@jprete,返回类型是字节码级别签名的一部分,正如我已经演示的那样。你如何支持你的陈述? – 2011-02-25 09:09:01

1

返回类型不是签名的一部分。

+0

+0:您对此声明有任何支持吗? – 2011-02-25 09:11:45

+0

Bruce Eckel“Thinking in Java”,第5章 - 方法重载,也谷歌认为相同http://download.oracle.com/javase/tutorial/java/javaOO/methods.html – 2011-02-25 13:14:03

1

这些是重复的方法。类型擦除意味着在运行时将Class<T>简化为Class,这意味着您有两种方法,每种都有lookupHome(Class homeClass)的签名。我的理解是,编译器不应该编译此代码;如果你有编译它的版本javac,那么有些东西是错误的!

最好的办法是重新命名两种方法,使它们每个都具有比lookupHome更具体的名称。

编辑:经过一段时间的语言规范,我认为这是很有可能的声明是合法的,虽然我不是100%确定。我仍然认为在擦除后有两个具有相同签名的方法是个不错的主意。

+0

示例代码在我们的项目中编译。这个项目是用十几个蚂蚁脚本编译的,我没有深入研究它是如何工作的。但它似乎编译器是org.eclipse.jdt.core.JDTCompilerAdapter – Sapience 2011-02-24 19:41:56

+0

它编译它,它的工作原理,因为他们确实有不同的sugnatures,看看字节码与'javap -c' – 2011-02-25 09:12:37

1

使用泛型时,这两种方法确实有不同的参数类型,所以编译器应该能够将它们分开。检查当前的语言规范,应该允许这两种方法。我听说在Java 7中,它们将不被允许,这在理论上是非常糟糕的。

要回答你的问题,是的,他们确实有不同的签名,基于参数类型。

但实际上,您应该避免这种混淆。有两个不同的方法名称。

public <T extends EJBHome> T lookupHome(Class<T> homeClass) 

public <T extends EJBLocalHome> T lookupLocalHome(Class<T> localHomeClass) 

重载是没有必要的。如有疑问,请使用不同的名称来突破重载。

+0

你是对的,使用不同的名字会更好。但是,我不是现有代码的作者,我只是不知道为什么Eclipse总是抱怨同样的擦除。我尝试使用javac进行编译,但也失败了。 – Sapience 2011-02-25 14:22:34

相关问题