【问题标题】:Inheritance + Interface problem继承+接口问题
【发布时间】:2009-10-27 16:09:41
【问题描述】:

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

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&lt;A&gt; 和一个子类 AButMore extends A。我想要AButMore extends A implements Prototypeable&lt;AButMore&gt;,但这是不允许的(不能用不同的类多次实现泛型接口)。还要注意AAButMore 都实现了一些其他接口,并且从AAButMore 的实现是相同的。

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

  • 伪装饰这两个类 - 即,有一个未实现 Prototypeable 接口的基类,从该基类继承到适当的子类,然后将这两个类扩展为它们自己的原型版本。缺点似乎是课程过多。

  • 不将A 扩展到AButMore,而是从As 构造AButMore 并委托所有复制的方法。然而,委托代码在我看来总是很愚蠢,尤其是当每个可以被继承的方法都将被委托而无需修改时。

  • 具有Prototypeable 指定Object 作为返回类型,并让工厂采用Class 参数进行转换。这里的缺点是,如果使用不当,这可能会导致不安全的强制转换。

编辑:澄清一下:目的是制造具有某种顺序依赖性的实例,而没有类变量。最简单的例子是,如果它们每个都有一个索引变量——basePrototype 将提供一个 0-index 实例,而 nextPrototype() 将提供一个 index+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 类了解不多。

【问题讨论】:

  • 如果(编辑)是这种情况,为什么不只是有一个接口 'Sequenced { int getIndex(); }'?
  • 这个想法是制造顺序实例,顺序依赖比索引更复杂——这只是可以完成的最简单的事情的一个例子。

标签: java generics inheritance interface


【解决方案1】:

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

public interface PrototypeMapping<U extends Prototypeable<Type>,V extends U>{
   public V mapTo(U u);
}

【讨论】:

  • 啊,不-我不是在寻找连续的类型,而是连续的实例。我试图在编辑中澄清我的问题。
【解决方案2】:

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

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

在这种情况下,您需要两个通用参数——一个用于原型的类型,一个用于下一个版本的类。根据您的第一个 sn-p,听起来更像是每个 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&lt;?&gt; 更具体。不过,这将有助于处理个别操作。

【讨论】:

  • 排序-我要定义的是类可以按顺序构建自己,据我所知,这意味着接口必须指定一个通用参数(要构建的类)。但是,我也有这样的情况,应该这样做的类在继承层次结构中(而不是在装饰器样式中 - 添加功能,而不仅仅是修改),这排除了使用不同参数多次实现接口。无论如何,我正在考虑用 AspectJ/AOP 风格的编程来解决这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-14
  • 1970-01-01
  • 2021-06-24
  • 2011-06-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多