我有以下工厂类。它有两个方法,它们接受Class实例并返回相应的对象。它们具有相同的方法名称,并且两个方法都将Class作为参数但具有不同的泛型类,也返回不同的类型。编译器是否认为这两个方法是重复的?当我在Eclipse中打开java文件时,它会报告如下错误:
描述资源路径位置类型 方法lookupHome(Class)具有与EJBHomeFactory EJBHomeFactory.java类型中的另一个方法相同的擦除lookupHome(Class)
但是,似乎javac不会报告任何错误。
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:这是使代码通过的ant脚本,我不确定它是如何工作的,但它不会抛出任何错误。看来编译器是Eclipse JDT Compiler,我尝试使用常规的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:我只是创建另一个示例,有两个基类,并且有两个子类,一个工厂类接受Class并根据参数的类型生成实例。代码无法由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);
}
}
因素类: import 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);
}
}
}
答案 0 :(得分: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);
}
}
编译并运行它并且您没有收到任何错误,现在将A中的getNum签名更改为
class A {
public static Integer getNum() { return 0; }
}
并且只编译类A.如果返回类型不是签名的一部分,这对B没有区别,但是如果你运行B而不重新编译它就会得到
Exception in thread "main" java.lang.NoSuchMethodError: A.getNum()Ljava/lang/Byte;
at B.main(B.java:10)
如您所见,返回类型Ljava/lang/Byte;
是返回类型的唯一名称,也是签名的一部分。
答案 1 :(得分:1)
返回类型不是签名的一部分。
答案 2 :(得分:1)
这些是重复的方法。类型擦除意味着Class<T>
在运行时减少到Class
,这意味着您有两个方法,每个方法都有签名lookupHome(Class homeClass)
。我的理解是编译器不应该编译这段代码;如果你有一个版本javac
来编译它,那么就出错了!
最好的办法是重命名这两种方法,使每种方法都具有比lookupHome
更具体的名称。
编辑:在语言规范中探讨了一段时间之后,我认为声明很可能是合法的,尽管我不是100%肯定。我仍然认为在擦除后有两个具有相同签名的方法是个坏主意。
答案 3 :(得分:1)
使用泛型,这两个方法确实有不同的参数类型,因此编译器应该能够将它们分开。检查当前的语言规范,应该允许这两种方法。我听说在Java 7中,它们是不允许的,这在理论意义上是非常糟糕的。
要回答你的问题,是的,他们确实有不同的签名,基于参数类型。
但实际上,你应该避免这种混乱。有两种不同的方法名称。
public <T extends EJBHome> T lookupHome(Class<T> homeClass)
public <T extends EJBLocalHome> T lookupLocalHome(Class<T> localHomeClass)
永远不需要重载。如有疑问,请使用不同的名称来中断重载。