3

我大致有这些类型:

interface Record {}
interface UpdatableRecord extends Record {}

interface Insert<R extends Record> {

    // Calling this method only makes sense if <R extends UpdatableRecord>
    void onDuplicateKeyUpdate();
}

我想<R>在调用时有一个额外的限制onDuplicateKeyUpdate()<R>这样做的原因是,这种方法只有在绑定到 的任何子类型时才有意义,UpdatableRecord而不仅仅是Record. 例子:

Insert<?>               i1;
Insert<Record>          i2;
Insert<UpdatableRecord> i3;

// these shouldn't compile
i1.onDuplicateKeyUpdate();
i2.onDuplicateKeyUpdate();

// this should compile
i3.onDuplicateKeyUpdate();

有没有什么技巧或方法可以用来为类的泛型类型添加一个额外的限制,只是为了一个方法声明?

注意

  • 声明Insert<R extends UpdatableRecord>不是一种选择,因为我希望拥有Insert<R extends Record>不可更新的记录的灵活性
  • 推导UpdatableInsert<R extends UpdatableRecord> extends Insert<R>该方法并将其向下推可能是一种选择,但我真的不想引入一种新类型。我有几个这样的方法有几个不同的限制,这会导致类型爆炸。
  • 投掷UnsupportedOperationException是相当明显的,但它似乎是 Java 1.4 的做法。我真的想知道我是否可以使用泛型和编译器来解决这个问题。

理想的解决方案

// This would be an annotation to be interpreted by the compiler. There is no such
// thing in Java, as far as I know. But maybe there's a trick having the same effect?
@Require(R extends UpdatableRecord)
void onDuplicateKeyUpdate();
4

2 回答 2

4

如果您在接口中定义它,那么它必须在任何希望实现该接口的类中实现。因此,您无法onDuplicateKeyUpdate在任何实现Insert<R extends Record>接口的类中实现。

您已经在接口中指定了它R extends Record,因此它成为合同的一部分,这意味着Record可以使用任何子类型。我想不出一种方法来使用将其限制为仅 type 的泛型添加额外的约束UpdatableRecord

此外,您无法根据类型在界面中做出决定,R因为该信息会因类型擦除而消失。

在我看来,您的问题围绕 和 之间的区别Record展开UpdatableRecord。由于接口意味着通用合同,我认为特定类型的行为不应该出现在您的接口中。因此,您必须想办法以另一种方式解决差异。我的解决方案是在调用的接口中实现一个方法(返回boolean),您可以在. 这样,如果记录不支持该操作或什么都不做(如果您想避免异常,并且在您的业务逻辑上下文中什么都不做有意义),您可以抛出一个。RecordcanUpdateonDuplicateKeyUpdateUnsupportedOperationException

这不是我的想法;可能有更好的解决方案。我会试着再想一想。

编辑

我对此进行了更多思考,正是您的最新编辑让我想到了这一点。<R extends Record>适用于整个班级。所以没有办法在特定方法的上下文中覆盖或缩小它。像您提到的那样的注释需要在编译器级别实现,并且在该级别我什至不确定您是否可以访问 R 的类型(因为类型擦除)。所以你说的可能是不可能的。

在概念层面上,您所要求的不应该是 IMO,因为您在合同指定了合同的例外情况。这听起来像是实现细节正在悄悄进入您的界面,因为界面不应该试图弄清楚什么可以或不能做什么;这应该在具体实施中决定。

于 2011-05-25T18:31:57.040 回答
1

Vivin 很好地说明了您在创建时签订的合同Insert。我会质疑onDuplicateKeyUpdate()在这种情况下是否真的有意义。它似乎只针对Record.

如果您可以将该功能滚动到另一种Insert方法中(例如execute()?),您可以在那里测试对象的类型并采取相应的行动。例如,

[pseudo-code]
method execute( R record )
 try
  insertRecord( record )
 catch ( KeyAlreadyExistsException e )
  if ( record instanceof UpdatableRecord )
   updateRecord( record )
  end if
 end try
end method

如果您正在寻找一种更加面向对象的方法,您可以将插入的逻辑推送到Record子类本身中。在那里,我会更改InsertInsertable,给它一个insert()方法,并Record实现Insertable。然后每个子类都Record可以知道自己的正确方法insert()。我不确定这是否符合您的设计策略...

于 2011-05-25T20:53:40.173 回答