9

这似乎是一个新问题,但我上次使用 Java 时,该语言没有泛型。我有一个类层次结构(名称更改为尽可能通用):

public abstract class AbstractBase { .... }
public class ConcreateSubA extends AbstractBase { .... }
public class ConcreateSubB extends AbstractBase { .... }
...
public class ConcreateSubZZ9PluralZAlpha extends AbstractBase { .... }
...

我正在尝试清理一些遗留代码,并且有一个地方可以通过泛型将大量重复重复全部分解为单个例程。(我在考虑泛型,因为当调用这个例程时,它只需要对一个具体类进行操作。)

例程看起来像

public <Thing extends AbstractBase> void someFunc()
{
    another_function_call (Thing.concreteSpecialToken);
    // could also be
    //   another_function_call (Thing.concreteSpecialToken());
    // if methods are more feasible than fields

    // Cannot use
    //   another_function_call (Thing().concreteSpecialToken());
    // because creating instances of these types is a Major Operation[tm]
}

我省略了无数行,但这是重要的部分: someFunc()是类型参数(它实际上需要参数,但它们都不是事物,所以没有推理)。最终我需要获取一个特殊的令牌,这就是我变得模糊的地方。

标记是每个具体类的巨大的唯一字符串。它们是基于类的,而不是基于实例的。实际的标记值private static final在每个子类中声明为一个字段。

所以我需要使用基类的公共方法/字段来(最终)获取子类的私有静态字段。显然我不能abstract static在基中声明一个方法,因为那没有意义。如果数据是基于实例的,那么这将是微不足道的,在基类中有一个多态的 getter,但子类的东西是静态的。

我觉得我在这里缺少 Java 泛型的一个特性,但我不能使用Thing.whatever(),除非它whatever是可以在抽象基类中声明的东西。我遇到了 Java 的限制或我缺乏试图弥合差距的专业知识。我所做的一个看起来很有希望的尝试也有大量的代码重复,一直到类层次结构,一遍又一遍地用完全相同的代码定义抽象方法......这就是泛型应该帮助防止的!

4

4 回答 4

7

我遇到了 Java 的限制或我缺乏试图弥合差距的专业知识。

这是 Java 的一个限制,尽管 IMO 是一个相当合理的限制。基本上,您仍然在尝试使用静态成员,就好像它们是多态的一样,这是行不通的——泛型在这方面对您没有帮助。

选项:

  • 使用反射......但请记住,类型擦除意味着您无法获取ClassforThing除非您明确传递它
  • 如果你一个Thing无论如何的实例,只需将其设为抽象实例成员,在每个实现中碰巧返回一个静态字段的值
  • 创建一个单独的类型层次结构,它将使用实例成员
于 2012-11-27T16:33:23.667 回答
3

如果我理解正确,您需要类型参数的具体类。通常的做法是像这样声明你的方法:public <T extends AbstractBase> void someFunc(Class<T> clazz)

这当然意味着需要向方法传递一个额外的参数,并且您需要使用反射来访问静态字段,但鉴于 Java 的类型擦除,这是唯一的方法。

这个故事的寓意是泛型和静态不能很好地结合在一起。

于 2012-11-27T16:32:30.200 回答
2

有点笨拙,但如果someFunc采用Class<Thing>参数,您可以使用反射:

public <Thing extends AbstractBase> void someFunc(Class<Thing> clz) {
  // exception handling omitted
  Object whatever = clz.getDeclaredMethod("whatever").invoke(null);

但是您可以通过使用嵌套类来更好地利用多态性,例如

public abstract class AbstractBase {
  public static class Info {
    public String getInfo() {
      return "AbstractBase";
    }
  }
}

public class ConcreteSubA extends AbstractBase {
  public static final Info INFO = new Info() {
    public String getInfo() { return "ConcreteSubA"; }
  }
}

并有someFunc一个AbstractBase.Info参数。

public <Thing extends AbstractBase> someFunc(AbstractBase.Info inf) {
  String info = inf.getInfo();
}

// call it as
ConcreteSubB csb = someFunc(ConcreteSubB.INFO);

这个想法是层次结构中的每个都有一个保存其以前静态数据的单例实例。Info

于 2012-11-27T16:32:49.420 回答
1

如果你想保持令牌原样,一个private static final字段,你可以通过反射得到它:

public <Thing extends AbstractBase> void someFunc(Class<Thing> clz)
{
    try {
        Field field = clz.getField("concreteSpecialToken");
        field.setAccessible(true);
        Object concreteSpecialToken = field.get(null);
        another_function_call (concreteSpecialToken);
    } catch (IllegalAccessException e) {
        handle(e);
    } catch (NoSuchFieldException e) {
        handle(e);
    }
}

在呼叫站点,您必须执行someFunc(ConcreateSubZZ9PluralZAlpha.class). 但是我想知道您是否可以这样做,为什么不将令牌对象作为参数传递,例如 someFunc(ConcreateSubZZ9PluralZAlpha.concreteSpecialToken)?或者甚至可以将方法移动someFunc()到令牌类本身。

于 2012-11-27T19:58:32.037 回答