Приведение отражений и перегрузка методов в Java - PullRequest
5 голосов
/ 28 марта 2012

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

У меня есть несколько классов, которые реализуют общий интерфейс.

public interface Inter{}
public class Inter1 implements Inter{}
public class Inter2 implements Inter{}

В отдельном классе у меня есть список типа Inter,который я использую для хранения и удаления типов Inter1 и Inter2 на основе пользовательского ввода.

java.util.ArrayList<Inter> inters = new java.util.ArrayList<Inter>();

У меня также есть семейство перегруженных методов, которые имеют дело с тем, как каждая реализация взаимодействует друг с другом, а также по умолчаниюреализация для 2 "Inter" s.

void doSomething(Inter in1, Inter in2){
    System.out.println("Inter/Inter");     
}
void doSomething(Inter1 in1, Inter1 in2){
    System.out.println("Inter1/Inter11");    
}
void doSomething(Inter2 in1, Inter1 in2){
    System.out.println("Inter2/Inter1");    
}

Методы периодически вызываются так:

for(int i = 0; i < inters.size() - 1; i++){
    for(int o = i+1; o < inters.size(); o++){
        Inter in1 = inters.get(i);    Inter in2 = inters.get(o);

        doSomething(in1.getClass().cast(in1), in2.getClass().cast(in2));

        System.out.println("Class 1: " + in1.getClass().getName());
        System.out.println("Class 2: " + in2.getClass().getName());
    }
}

Пример вывода из этого:

Inter/Inter
Class 1: Inter
Class 2: Inter
Inter/Inter
Class 1: Inter
Class 2: Inter1
Inter/Inter
Class 1: Inter1
Class 2: Inter1

Глядя на вывод, становится ясно, что doSomething (Inter in1, Inter in2) вызывается даже в тех случаях, когда должны быть вызваны другие методы.Интересно, что выведенные имена классов являются правильными.

Почему в java есть статическая перегрузка методов, когда типы классов определяются во время выполнения с помощью отражения?Есть ли способ заставить Java сделать это?Я знаю, что могу использовать рефлексию, а также Class.getMethod () и method.invoke (), чтобы получить желаемые результаты, но было бы намного лучше сделать это с кастингом.

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

Ответы [ 2 ]

7 голосов
/ 28 марта 2012

Мне кажется, мы говорим о том, что происходит:

doSomething(in1.getClass().cast(in1), in2.getClass().cast(in2));

Исходя из вашего удивления, что тип выводимого всегда Inter, кажется, что вынемного запутался в том, что здесь происходит.В частности, вы, кажется, думаете, что in1.getClass().cast(in1) и in2.getClass().cast(in2) должны вызывать различную перегрузку из-за их различного типа времени выполнения.Однако это неверно.

Устранение перегрузки метода происходит статически.Это означает, что это происходит на основе объявленных типов двух аргументов метода.Поскольку оба значения in1 и in2 объявлены как Inter, очевидно, что выбран метод void doSomething(Inter in1, Inter in2).

Вывод заключается в том, что in1 объявлен как Inter.Это означает, что in1.getClass() по существу совпадает с Inter.class для целей статического анализа - getClass просто возвращает Class<? extends Inter>.Следовательно, забрасывание бесполезно, и вы получите только первую перегрузку.

1 голос
/ 28 марта 2012

Спецификация языка Java (JLS) в разделе 15.12 Выражение вызова метода подробно объясняет процесс, которым следует компилятор, чтобы выбрать правильный метод для вызова.

Там вы заметите, что это задача компиляции .JLS говорит в подразделе 15.12.2:

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

В вашем случае это означает, что, поскольку вы передаете два объекта типа Integerнаиболее конкретным методом является тот, который получает именно это.

Чтобы проверить природу этого во время компиляции, вы можете выполнить следующий тест..

public class ChooseMethod {
   public void doSomething(Number n){
    System.out.println("Number");
   }
}

Объявите второй класс, который вызывает метод первого и скомпилирует его.

public class MethodChooser {
   public static void main(String[] args) {
    ChooseMethod m = new ChooseMethod();
    m.doSomething(10);
   }
}

Если вы вызываете main, вывод говорит: Number.

Теперь добавьте второй более конкретный метод в класс ChooseMethod и перекомпилируйте его (но не перекомпилируйте другой класс).

public void doSomething(Integer i) {
 System.out.println("Integer");
}

Если вы снова запустите main, выводвсе еще Number.

В основном, потому чтопотому что это было решено во время компиляции.Если вы перекомпилируете класс MethodChooser (класс с основным) и снова запустите программу, вывод будет Integer.

Таким образом, если вы хотите принудительно выбрать один изперегруженные методы, тип аргументов должен соответствовать типу параметров во время компиляции, а не только во время выполнения, как вы ожидаете в этом упражнении.

...