Делают ли параметры метода с разными обобщениями методы разными сигнатурами? - PullRequest
1 голос
/ 24 февраля 2011

У меня есть следующий заводской класс.У него есть два метода, которые принимают экземпляры класса и возвращают соответствующий объект.У них одинаковое имя метода, и оба метода принимают Class в качестве параметра, но с другим универсальным классом, также возвращают разные типы.Считает ли компилятор эти два метода дублирующимися?Когда я открываю java-файл в Eclipse, он сообщает об ошибках, таких как:

Описание Путь к ресурсу Тип местоположения Метод lookupHome (Class) имеет тот же стирающий lookupHome (Class), что и другой метод типа EJBHomeFactory EJBHomeFactory.java

Однако, похоже, что 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: я просто создаю другой пример, имею два базовых класса и два подкласса, фабричный класс принимает класс и генерирует экземпляр на основе типа параметра.Код не может быть скомпилирован 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);
        }
    }
}

Ответы [ 4 ]

3 голосов
/ 24 февраля 2011

Это работает, когда метод может быть разрешен во время компиляции, потому что возвращаемый тип является частью подписи. После стирания у вас есть два метода

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

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 голос
/ 25 февраля 2011

При использовании обобщений оба метода имеют разные типы аргументов, поэтому компилятор должен иметь возможность их разделять. Проверка текущей спецификации языка, два метода должны быть разрешены. Я слышал, что в Java 7 они не будут разрешены, что довольно плохо в теоретическом смысле.

Чтобы ответить на ваш вопрос, да, у них есть разные подписи, основанные на типах аргументов.

Но практически, в любом случае, вам следует избегать такой путаницы. Иметь два разных имени метода.

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

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

Перегрузка не нужна. В случае сомнений прекратите перегрузку, используя разные имена.

1 голос
/ 24 февраля 2011

Это дублирующие методы. Стирание типа означает, что Class<T> уменьшается до Class во время выполнения, что означает, что у вас есть два метода, каждый из которых имеет сигнатуру lookupHome(Class homeClass). Насколько я понимаю, компилятор не должен компилировать этот код; если у вас есть версия javac, которая ее компилирует, значит что-то не так!

Лучше всего переименовать оба метода, чтобы у каждого из них было более конкретное имя, чем lookupHome.

РЕДАКТИРОВАТЬ: Поработав некоторое время в спецификации языка, я думаю, что вполне возможно, что декларация законна, хотя я не уверен на 100% Я до сих пор считаю плохой идеей иметь два метода с одинаковыми сигнатурами после стирания.

1 голос
/ 24 февраля 2011

Тип возврата не является частью подписи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...