开发者

Does the method parameters with different generics make the methods have different signature?

开发者 https://www.devze.com 2023-02-13 10:51 出处:网络
I have the following factory class. It has two methods which take Class instances and return the corresponding object. They have the same method name, and both methods take Class as parameter but with

I have the following factory class. It has two methods which take Class instances and return the corresponding object. They have the same method name, and both methods take Class as parameter but with different generic class, also return different types. Does the compiler think these two methods as duplicate? When I open the java file in Eclipse, it reports errors like:

Description Resource Path Location Type Method lookupHome(Class) has the same erasure lookupHome(Class) as another method in type EJBHomeFactory EJBHomeFactory.java

However, it seems the javac won't report any error on it.

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 ... */
}

Update 1: Here is the ant script th开发者_如何学Cat makes the code pass, I'm not sure how it works, but it doesn't throw any errors. It seems the compiler is Eclipse JDT Compiler, I tried with the regular javac, and it doesn't compile.

<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>

Update 2: I just create another example, have two base classes, and have two subclasses, a factory class takes Class and generate instance based on the parameter's type. The code can't be compiled by javac, and in Eclipse, the IDE complains about the same erasure problem. Here is the code: Two empty base classes:

public class BaseClassFirst {
}

public class BaseClassSecond {
}

Two subclasses:

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

Factor class: 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);
        }
    }
}


This works when the method can be resolved at compile time because the return type is part of the signature. After erasure you have two methods

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

You cannot define these without generics but as the return type is part of the signature, these are different methods.

You can call

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

but not

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

EDIT: Try the following.

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

And you should see something like

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

Similarly, if you use javap -c.

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

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

EDIT If you want an example of where the return type is part of the signature..

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

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

Compile and run this and you get no error, now change the signature of getNum in A to

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

and compile only the class A. If the return type was not part of the signature, this would make no difference to B, however if you run B without recompiling it you get

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

As you can see, the return type Ljava/lang/Byte; is the inetrnal name of the return type and part of the signature.


Return type isn't part of a signature.


With generics, the two methods do have different argument types, so compiler should be able to separate them. Checking the current language spec, the two methods should be allowed. I heard that in Java 7, they will not be allowed, which is pretty bad in theoretical sense.

To answer your question, yes, they do have different signatures, based on argument types.

But practically, you should avoid such confusion anyway. Have two different method names.

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

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

Overloading is never necessary. When in doubt, break overloading by using different names.


These are duplicate methods. Type erasure means that Class<T> is reduced to Class at runtime, which means that you have two methods that each have the signature lookupHome(Class homeClass). My understanding is that the compiler should not compile this code; if you have a version of javac that compiles it, then something is wrong!

Your best bet is to rename both methods so that they each have a more specific name than lookupHome.

EDIT: After poking around in the language spec for a while, I think it is very possible that the declaration is legal, although I'm not 100% sure. I still think it's a bad idea to have two methods with identical signatures after erasure.

0

精彩评论

暂无评论...
验证码 换一张
取 消