10

我想在列表中运行某些测试。列表可以包含完全不同的类。

我有一种方法来检查列表的一致性 - 不为空,不为空,不超过 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) {...}

编译器错误 - “方法检查(列表)与另一个方法具有相同的擦除检查(列表)......”

那么有什么优雅的解决方案吗?我可以只使用不同的方法名称,但想知道没有它怎么可能。

谢谢!

4

5 回答 5

10

这与您缺少的泛型无关。Java 没有双重分派。调用check必须在编译时解决,并且check(T)是唯一的匹配项,因为编译器无法判断T是在给定场景中SomeType还是SomeOtherType在给定场景中。它需要选择一种方法来调用,该方法适用于所有可能T的 s。

这有时可以使用访问者模式来解决。

于 2013-01-30T16:00:05.747 回答
4

您可以使用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
  }
} 
于 2013-01-30T16:00:59.640 回答
4

问题应该由调用者解决。当它使用 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
    }
});
于 2013-01-30T16:02:46.540 回答
2

使用泛型,类型参数实际上在编译期间被擦除,并且列表对象不知道它包含的对象的静态类型。既然不知道,就不能用重载来调用不同参数的方法,因为Java不支持多分派

那么你有三个选择:

  • 使您的对象使用执行Checked检查逻辑的检查方法实现接口。缺点是检查逻辑现在分散在几个地方,如果你有你无法控制的类的对象,这是不切实际的。
  • 用于根据对象的动态类型instanceof显式调用方法。check不利的一面是,您最终可能会遇到一个难以维护的大 if/else 块。
  • 实现访问者模式。缺点是您也必须更改对象类,但check逻辑保留在一个地方。
于 2013-01-30T16:01:12.847 回答
1

由于变量的类型丢失了,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);
  }

这可以变得更复杂一些,例如通过将每个检查包装在 a 中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();
    }
}

这是仅有的两个选项,任何实现都必须是其中之一的变体

于 2013-01-30T18:27:06.993 回答