在派生类上调用时,在Java

时间:2015-11-16 13:46:58

标签: java google-app-engine generics objectify

我有一个与数据存储区交互的实用程序类(在我的情况下是GAE的内置数据存储区),它有以下方法:

//Class GaeDataUtil
public static <T> Optional<Key<T>> saveEntity(T entity)

Optional来自Guava图书馆,Key<T>来自Objectify,尽管我怀疑其中任何一个都有所不同。)

我希望我的(最小)实体层次结构具有.save()方法。因此:

public class User extends RootEntity

RootEntity提供的地方:

public Optional<Key<T>> save() {
    //Skipping the error-handling.
    return GaeDataUtil.saveEntity(this);
}

我可以写:

User myUser = new User();
// set some properties
Optional<Key<User>> optKey = myUser.save();

但当然这不起作用,因为对myUser.save()的调用会根据需要返回Optional<Key<RootEntity>>而不是Optional<Key<User>>

我可以通过在User.save()(以及Account.save()Project.save()等等)中进行类型转换来避免此问题并抑制警告,但即使只有(例如)10个实体类扩展RootEntity,这仍然是一些样板代码,只需将写入类型转换。此外,我认为如果我必须为每个派生类编写代码(尽管很小),那么拥有类层次结构的许多好处都会丢失(也会有其他类似的方法)。

有更好的解决方案吗?

使用Java 7

更新

3 个答案:

答案 0 :(得分:2)

您只需要在T方法中将其强制转换为通用类型RootEntity.save()

public <T> Optional<Key<T>> save() {
    //Skipping the error-handling.
    return (Optional<Key<T>> GaeDataUtil.saveEntity(this); // This line will generate a warning.
}

然后当你写作时,

Optional<Key<User>> optKey = myUser.save();

由于Target Type Inference,它会自动正确推断。

答案 1 :(得分:1)

一种解决方案是参数化RootEntity这样的事情:

class RootEntity<Subclass extends RootEntity> {
  public Optional<Key<Subclass>> save() {...}
}

然后定义您的子类,如:

class User extends RootEntity<User> {...}

之前我使用过这种模式。如果有一个更光滑的解决方案,我会渴望看到它。 :)

答案 2 :(得分:1)

这是最终奏效的:

createStore

通过两个步骤执行此操作(获取public <T extends RootEntity> Optional<Key<T>> save1() { @SuppressWarnings("unchecked") Key<T> key = (Key<T>) ofy().save().entity(this).now(); return Optional.fromNullable(key); } ,然后将其打包在Key)---它让目标类型推断正常工作。只需一步即可完成:

Optional

@Codebender建议的第二种形式显示错误(public <T extends RootEntity> Optional<Key<T>> save2() { return (Optional<Key<T>>) Optional.fromNullable(ofy().save().entity(this).now()); } ),而不是Eclipse中的警告。

Target Type Inference works if done in two steps

然而,@ Codelder使用目标类型推断的基本想法是合理的。