1

在为存储库(例如 Web 服务或数据库)编写类型处理程序时,我需要在从存储库加载值后实例化类型。

假设我String从存储库中获取了一个值,并且有一个String可以使用的带有一个参数的构造函数。如果返回类型有类型参数,除了实例化原始类型还能做什么?似乎原始类型的存在只是为了与遗留代码兼容,所以我不想使用它们。

一般 ?可以用作类型参数(如果您知道该类型在运行时将是正确的),但在这种情况下不能使用,因为您不能使用通配符作为类型参数来实例化类。

编辑:一些示例代码:

假设我有一个这样的 PrimaryKey 类:

public class PrimaryKey<R extends RepositoryObject<R>> {
    private String value;
    public PrimaryKey(String value) {
        this.value = value;
    }
}

以及一组扩展 RepositoryObject 的类,其定义如下:

public class RepositoryObject<R extends RepositoryObject<R>> {
    private PrimaryKey<R> pk;
    public RepositoryObject(PrimaryKey<R> pk) {
        this.pk = pk;
    }
    PrimaryKey<R> getPrimaryKey() {
        return pk;
    }
}

子类示例:

public class User extends RepositoryObject<User> {
    public User(PrimaryKey<User> userId) {
        super(userId);
    }
}

现在,PrimaryKey 类的类型处理方法将如下所示:

public PrimaryKey<?> getValue(String stringValue) {
    return new PrimaryKey<>(stringValue);
}

但这会导致编译器错误(在 Maven 构建中,而不是在 Eclipse IDE 中),即使我在实例化时使用的是菱形运算符。由于类型参数中的递归,可能由于某种原因类型推断不能很好地工作。

4

2 回答 2

1

在 Java 7 中,您通常可以使用菱形运算符来解决此限制

Container<?> c = new Container<>(arg);

否则,您可以使用辅助工厂方法:

<T> Container<T> makeContainer(String arg) {
    return new Container<T>(arg);
}

...

Container<?> c = makeContainer(arg);

编辑:

在您更新之后,我可以看到您正在使用递归类型参数<R extends RepositoryObject<R>>。此编译错误是由于 javac 在通配符捕获和递归类型参数方面的限制。请参阅此相关帖子,例如:Java CRTP and Wildcards: Code compiles in Eclipse but not `javac`

不幸的是,使用原始类型作为一种解决方法是必要的,但它可以作为实现细节隐藏:

public PrimaryKey<?> getValue(String stringValue) {
    @SuppressWarnings("rawtypes") //a raw type is necessary to placate javac
    final PrimaryKey<?> pk = new PrimaryKey(stringValue);
    return pk;
}
于 2013-04-02T14:18:37.430 回答
0
class SomeBogusClass extends RepositoryObject<SomeBogusClass> { }

return new PrimaryKey<SomeBogusClass>(stringValue);

说真的,你可以把任何满足界限的东西放在那里,甚至是一些与你的代码无关的虚假类。

于 2013-04-02T18:48:56.910 回答