继承+接口问题

时间:2009-10-27 16:09:41

标签: java generics inheritance interface

我有一个界面来描述一个类何时可以创建自己的“下一个”版本:

public interface Prototypeable<Type extends Prototypeable<Type>> {
 public Type basePrototype(); // the zeroth raw instance of Type
 public Type nextPrototype(); // the next instance of Type
}

一起使用
public class Prototyper {
 public static <Type extends Prototypeable<Type>> List<Type> prototypeFactor(int numberOfInstances, Type proto) {
  List<Type> result = new ArrayList<Type>(numberOfInstances);
  Type holder = proto.basePrototype();
  result.add(holder);
  for (int i=1; i<numberOfInstances;i++) result.add(holder = holder.nextPrototype());
  return result;
}

现在,我有一个基类A implements Prototypeable<A>和一个子类AButMore extends A。我想拥有AButMore extends A implements Prototypeable<AButMore>,但这是不允许的(不能用不同的类多次实现泛型接口)。另请注意,AAButMore都实现了其他一些接口,并且从AAButMore的实现完全相同。

有关解决此问题的建议?我似乎无法摆弄通用问题,所以我考虑了一些替代设计:

  • 伪装饰两个类 - 即,具有不实现Prototypeable接口的基类,从该类继承到正确的子类,然后将这两个类扩展到自身的Prototypeable版本。缺点似乎是大量的课程。

  • 未将A扩展为AButMore,而是从AButMore构建A并委派所有复制的方法。但是,代理代码对我来说似乎总是很愚蠢,特别是当每个可以继承的方法都将被委托而不进行任何修改时。

  • Prototypeable指定Object作为返回类型,并让工厂采用Class参数进行投射。这里的缺点是,如果使用不当,这可能会导致不安全的演员表。

编辑:澄清一下:目的是制造具有某种顺序依赖性的实例,而不使用类变量。最简单的例子是,如果它们各自都有一个索引变量 - basePrototype将提供一个0索引实例,而nextPrototype()将提供一个索引+ 1实例(基于调用该方法的实例的索引)。这种特殊情况有点过于简单(并且可能以更简单的方式实现),但涵盖了这个想法。

编辑:有关进一步说明,以下是目前的确切实施(我正在使用上述第三种方案):

public class BuildFromPrototype {
 public static <T extends Prototypeable> List<T> build(int buildCount, Class<T> protoClass, T prototype) {
  if (protoClass==null || prototype==null || buildCount<=0) return null;
  if( protoClass.isInstance(prototype.basePrototype()) && protoClass.isInstance(prototype.nextPrototype()) ) {
   List<T> result = new ArrayList<T>(buildCount);
   T pHolder = protoClass.cast(prototype.basePrototype());
   result.add(pHolder);
   for (int i=1;i<buildCount;i++)
    result.add(pHolder = protoClass.cast(pHolder.nextPrototype()));
   return result;
  } else return null;
 }

 public interface Prototypeable {
  public Object nextPrototype();
  public Object basePrototype();
 }
}

我认为这会处理误用(返回null是一个选项,Exception也是合理的),但测试有效的强制转换可能会很昂贵。这种形式的铸造也可能很昂贵 - 我对Class类不太了解。

2 个答案:

答案 0 :(得分:1)

我不确定你想要做什么来定义“下一个”版本,它看起来你想做元类编程,你不能在java中做,即我看不出你怎么能有泛型类型系统管理在运行时确定的一系列类型,因为它们是类型擦除的并且在运行时不存在。定义从一种类型到下一种类型的映射的接口如何,例如

之类的东西
public interface PrototypeMapping<U extends Prototypeable<Type>,V extends U>{
   public V mapTo(U u);
}

答案 1 :(得分:0)

可以在Java的泛型中表达的唯一关系是超/子类型关系。对我来说,这听起来并不像你想要越来越多的特定类(即子类型),而是相同界面的“兄弟”实现。你不能单独使用纯泛型类型来表达它 - 你不能注释类,以便Java知道MyImplB是MyImplA的“下一个”实现。

您可以传达此信息的最简单方法是使用类文字,就像您在上一个案例中所做的那样。使用此方法,您可以严格限制下一个实现的类型。但是,根据您要执行的操作,通过isInstance进行的运行时检查可能不是最有用的限制;编译时检查通常是更好的选择。

在这种情况下,您需要使用两个通用参数 - 一个用于原型的类型,另一个用于下一个版本的类。基于您的第一个片段,听起来更像是每个Type也应该使用下一个版本的类型进行参数化:

/**
 * @param N the specific class of the next type
 */
public class/interface Type<N extends Type>
{
   public Type<?> basePrototype();
   public N nextPrototype();
}

public class MyImplA implements Type<MyImplB> { ... }
public class MyImplB implements Type<MyImplC> { ... }
// ... and so on

然后,这将静态强制执行不同类型实现之间的链接。

我不确定在你所展示的情况下会对你有多好,因为对于类型安全的异类容器并没有太多的支持。由于您将所有内容放在ArrayList中,因此您无法断言列表的内容比Type<?>更具体。但是,它有助于处理个人操作。