4

我在 Wicket 1.3 -> Wicket 1.4 迁移时遇到了一些问题,但是这个问题也可以应用于 Java 泛型。迁移已经导致数百个警告突然出现——对于那些不熟悉 Wicket 的人来说,许多 Wicket 类都来自一个共同的祖先,它在 v1.4 中被泛化——我不确定要应用哪些参数在某些情况下,主要是各种表格和表格。我认为他们可以使用<?>,<Object><Void>,但我不确定是哪个。

<?>似乎最适合我,但有很多地方我不能使用通配符。 <Object>在所有情况下都有效,但这让我感到不安,因为它基本上是在不使用通配符的情况下编写通配符,这对我的部分大脑来说是天生错误的。Wicket 迁移指南<Void>中建议使用。

那么在这种情况下,正确的做法是什么?


编辑 2:我认为我的第一次编辑(现在在问题的底部)使人们看起来像是在询问字符串的集合,从而使人们感到困惑。以下是其他示例及其警告:

public class DocumentProcessor extends Form implements DocumentManagement { ...

表单是原始类型。对泛型类型 Form 的引用应该被参数化

AjaxFallbackDefaultDataTable theTable = new AjaxFallbackDefaultDataTable("theTable", cols, dataProvider, recPerPg);

此行的多个标记
- 类型安全:构造函数 AjaxFallbackDefaultDataTable(String, List, ISortableDataProvider, int) 属于原始类型 AjaxFallbackDefaultDataTable。对泛型类型 AjaxFallbackDefaultDataTable 的引用应该被参数化
- AjaxFallbackDefaultDataTable 是一个原始类型。对泛型类型 AjaxFallbackDefaultDataTable 的引用应该被参数化
- AjaxFallbackDefaultDataTable 是一个原始类型。应该参数化对泛型类型 AjaxFallbackDefaultDataTable 的引用


编辑:我希望让问题如此广泛以至于不需要示例代码,但这里有一些。

List<IColumn> columns = new ArrayList<IColumn>();
columns.add(new PropertyColumn(new Model<String>("Number"), "revisionID"));

生成这些警告:

[第一行] 处有多个标记
- IColumn 是原始类型。应该参数化对泛型类型 IColumn 的引用
- IColumn 是原始类型。应该参数化对泛型类型 IColumn 的引用

[the second] 行的多个标记
- 类型安全:构造函数 PropertyColumn(IModel, String) 属于原始类型 PropertyColumn。应参数化对泛型类型 PropertyColumn 的引用
- PropertyColumn 是原始类型。应参数化对泛型类型 PropertyColumn 的引用

没有错误。

4

6 回答 6

3

如果您不打算使用组件的底层模型对象,请使用Void 。

从语义上讲,它更合理,并且更好地传达了模型对象不是可以是任何东西,而是它在语义上什么都不是并且永远不会被使用的想法。在这种情况下,Void 关键字主要用作常规解决方案。

如果您要使用模型对象并且不在乎,我认为这不是您的意思,**在可以使用通配符的地方**和在不能使用的地方(构造函数参数等), Void、Object 或其他一些“包含”类,根据组件的特定语义和所需的泛型类型行为做出决定(例如,在组件变量的构造函数的情况下,您会考虑您的构造函数将使用 Void 或 Object 类型做什么)。

当然,那是从“良好的编程实践”理论来看,在实践中你不必太在意,虽然这种思维可以帮助你的队友维护你的代码,并且可以帮助你更好地理解它,甚至可以预测错误.

在 wicket 用户中到处使用通配符是相当普遍的,可能比我建议的决定更常见,但这并不是因为通配符是惯例,而是很可能仅仅是因为搜索引擎中出现的大多数代码示例更喜欢通配符。尽管如此,正如迁移指南对 Void 的建议所示,通配符不仅在语义上不太连贯,而且它们似乎也不是一个绝对的约定,甚至似乎受到 wicket 开发人员的挑战,我们最好假设,足够了解他们类型的内部运作,以便认真对待他们的建议。

于 2012-05-15T09:50:40.273 回答
1

从语义上讲,使用<?>意味着“我不知道类型,实际上我根本不在乎。使用其他任何东西都会对预期内容的形式设定期望。实际上,<Object>也是如此,但声明您将使用属性使用参数类型的泛型。

所以经验法则应该是:

  • 如果您只处理遗传对象而不使用它的参数化内容,请使用<?>以便您一眼就知道该参数与行为无关。
  • 在任何其他情况下,请使用包含您的方法设计使用的所有类型的最具体的参数。极端情况是<Object>,其他包括<? extends SomeTopLevelType>
于 2009-12-02T23:47:52.203 回答
1

可用的替代方案是:

  1. 要仅使用原始类型,就像您在示例代码中所做的那样,只需忽略警告
  2. 使用通配符/对象泛型
  3. 使用扩展泛型

我从你的问题中假设 #1 对你来说不是一个可行的选择。

#2 的示例(通配符/对象)

List<IColumn<?>> columns = new ArrayList<IColumn<?>>();

或者

List<IColumn<Object>> columns = new ArrayList<IColumn<Object>>();

IMO 我认为您是否选择?或并不重要Object,而且两者都不比另一个更正确,至少在功能上是这样。
如果您不关心泛型是什么,并且您从不访问它,那么它的后果很严重;尽管请仔细考虑,如果确实有可能您将来会在这里使用泛型。只有在您的预迁移代码中,您发现自己不必从IColumn对象内进行任何类型转换,才会出现这种情况。

#3 的示例(扩展通用)

为该类型的所有可能的泛型创建超类型或公共接口IColumn。哪里
T extends MyType

List<IColumn<T>> columns = new ArrayList<IColumn<T>>();

我会根据实际可能的通用属性来决定在第二种和第三种方法之间进行选择IColumn

  • 如果它们是您自己的类并且您实际上想要访问泛型类型的对象,我会选择第三种方法,
  • 否则,例如使用String或盒装原语Integer,或者如果您不使用泛型类型的对象,我会选择方法 2。

高温高压

于 2009-12-01T12:41:09.140 回答
0

我没有使用检票口,沃达丰阻止我查看 API 文档。但是,您似乎缺少许多通用参数并想要类似的东西:

List<IColumn<String>> columns = new ArrayList<IColumn<String>>();
columns.add(new PropertyColumn<String>(new Model<String>("Number"), "revisionID"));

如果您想添加其他IColumn带有不相关的泛型参数的 s,您将需要类似的东西;

List<IColumn<?>> columns = new ArrayList<IColumn<?>>();
columns.add(new PropertyColumn<String>(new Model<String>("Number"), "revisionID"));

或者,如果您需要获取列的属性,可能类似于:

List<IColumn<String>> strColumns = new ArrayList<IColumn<String>>();
List<IColumn<?>> columns = new ArrayList<IColumn<?>>();
PropertyColumn<String> column =
    new PropertyColumn<String>(new Model<String>("Number"), "revisionID");
strColumns.add(column);
columns.add(column);
于 2009-12-01T06:03:18.933 回答
0

您想使用与您的组件关联的模型类型。也就是说,使用调用 getModelObject() 返回的类型。因此,使用迁移指南中的示例:

ListView<Person> peopleListView = new ListView<Person>("people", people) {
    protected void populateItem(ListItem<Person> item) {
        item.add(new Link<Person>("editPerson", item.getModel()){
            public void onClick() {
                Person p = getModelObject();
                setResponsePage(new EditPersonPage(p));
            }
        });
    }
};

使用泛型很容易看出这是一个人员列表,并带有指向使用人员作为模型的编辑页面的链接。不幸的是,在检票口中,您的组件通常没有与之关联的模型。在这种情况下,getModel() 将返回 null,因此要使用的正确类型是<Void>,它本质上是 null 的占位符。

文档处理器

public class DocumentProcessor extends Form implements DocumentManagement { ...

如果您没有为 DocumentProcessor 设置模型,它看起来像这样:

public class DocumentProcessor extends Form<Void> implements DocumentManagement {
    public DocumentProcessor(String id) {
        super(id);
        ....

但是使用模型 DocumentProcessor 看起来像这样:

public class DocumentProcessor extends Form<Document> implements DocumentManagement {
    public DocumentProcessor(String id, Document doc) {
        super(id, doc);

AjaxFallbackDefaultDataTable

从它的构造函数来看,AjaxFallbackDefaultDataTable 可能会在它的模型中存储一个 IColumn[] 或 List ,但是对于您不知道或不关心的实现,这<?>是合适的,这和 DocumentProcessor 之间的区别在于您正在扩展 Form 并因此做知道并关心它是如何使用它的模型的。

对于 IColumn/PropertyColumn 示例,我将假设 revisionID 字段是 Long,然后我会这样写:

List<PropertyColumn> columns = new ArrayList<PropertyColumn>();
columns.add(new PropertyColumn<Long>(new Model<String>("Number"), "revisionID"));

你可能会看

更多 1.4 迁移信息

空类型参数

于 2009-12-03T22:39:33.377 回答
-1

警告说IColumn接口和PropertyColumn类是参数化类型,因此您只需为它们定义类型参数。

考虑以下示例:

List<Set> list = new ArrayList<Set>();

ListArrayList是参数化类型,并且定义了它们的类型参数。但是,设置也是参数化类型,但它的原始版本用作类型参数,因此,编译器会在这种情况下生成警告。

我们可以通过显式指定所有类型参数来修复我们的示例,例如

List<Set<Integer>> list1 = new ArrayList<Set<Integer>>();
List<Set<String>> list1 = new ArrayList<Set<String>>();

你需要为你的泛型类和接口做同样的事情。

于 2009-12-01T05:54:46.467 回答