Перегрузка / дженерики в Java

Я хочу запустить определенные тесты в списках. Списки могут содержать совершенно разные классы.

у меня есть один метод для проверки согласованности списка - не null, не пустой, не более x элементов. Это общее для всех списков. Затем я хочу проверить каждый из объектов, используя перегрузку.

идея была бы чем-то вроде:

public static <T> void check(List<T> list) {

    //do general checks

    for (T element : list) {
        check(element);
    }
}

а то

public static void check(SomeType element) {...}

public static void check(SomeOtherType element) {...}

но мне также пришлось добавить метод, как это:

public static void check(T element) {...}

и это было вызвано во время выполнения-не мои другие методы с конкретными классами. Хотя класс был точно таким же. Очевидно, мне не хватает понимания дженериков.

теперь, если я не использую общий метод Вообще и пытаюсь решить его таким образом:

    public static void check(List<SomeType> list) {...}

    public static void check(List<SomeOtherType> list) {...}

ошибка компилятора- " проверка метода(список) имеет ту же проверку стирания (список), что и другой метод..."

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

спасибо!

5 ответов


так как тип переменной теряется в check(List<T> list) у вас есть два варианта:

1. Делайте разные вещи, проверяя тип выполнения

check(T element) {
  if (element.getClass().equals(SomeType.class)) {
    check((SomeType) element);
  } elseif (element.getClass().equals(SomeOtherType.class)) {
    check((SomeOtherType) element);
  }

это можно сделать немного более сложным, например, обернув каждый чек в Callable и с помощью Map<Class, Callable>

это похоже на паттерн посетитель.

2. Вызов виртуального метода для элемента, который нужно проверить сам

если логика проверки можно подтолкнуть к объекту, который нужно проверить сам (это не обязательно плохо), тогда вам не нужно проверять типы:

interface Checkable { void check(); }
class SomeType implements Checkable { .... }
class SomeOtherType implements Checkable { .... }

затем:

public static <T extends Checkable> void check(List<T> list) {
    for (T element : list) {
        element.check();
    }
}

это только два варианта, любая реализация должна быть вариацией на одном из них


это не то, что дженерики, что ты пропал. Java не имеет двойной диспетчеризации. Вызов check должно быть разрешено во время компиляции, и check(T) является единственным совпадением, так как компилятор не может сказать, если T и SomeType или SomeOtherType в данном сценарии. Он должен выбрать один метод для вызова, который будет работать для всех возможных Ts.

это иногда решается с помощью шаблон Visitor.


проблема должна быть решена вызывающим абонентом. Когда он создает экземпляр вашего класса с конкретным типом для T, он также должен передать экземпляр Checker<T> с тем же конкретным типом:

public class SomeClass<T> {
    private List<T> list;
    private Checker<T> checker;

    public SomeClass(Checker<T> checker) {
        this.checker = checker;
    }

    public void check() {
        checker.check(list);
    }
}

public interface Checker<T> {
    public void check(List<T> list);
}

...

SomeClass<Foo> someClass = new SomeClass<Foo>(new Checker<Foo>() {
    @Override 
    public void check(List<Foo> list) {
        // do whatever you want here
    }
});

можно использовать instanceof направить:

public static <T> void check(List<T> list) {
  for (T element : list) {
    check(element);
  }
}

public static void check(T t) {
  if (t instanceof SomeType) {
    SomeType someType = (SomeType) t;
    // code for SomeType ...
  } else if (t instanceof OtherType) {
    OtherType otherType = (OtherType) t;
    // code for OtherType ...
  } else {
    // we got a type that we don't have a method for
  }
} 

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

тогда у вас есть три варианта:

  • Сделайте ваши объекты реализовать Checked интерфейс с методом, который проверяет логика. Недостатком является то, что логика проверки теперь рассеяна в нескольких местах, и это не практично, если у вас есть объекты классов, которые вы не контролируете.
  • использовать instanceof для явного вызова check методы в соответствии с динамическим типом объекта. Недостатком является то, что вы потенциально получаете большой блок if/else, который немного сложнее поддерживать.
  • реализовать шаблон Visitor. Недостатком является то, что вам также нужно изменить классы объектов, но check логика пребывания в одном месте.