在模糊的情况下应该使用哪种Java泛型?

时间:2009-12-01 03:59:50

标签: java generics wicket

我在使用Wicket 1.3时遇到了一些问题 - &gt; Wicket 1.4迁移,但这个问题也可以整体应用于Java泛型。迁移导致数百个警告突然冒出来 - 对于那些不熟悉Wicket的人来说,许多Wicket类派生于一个共同的祖先,它在v1.4中变得普遍 - 而且我不确定应用哪些参数在某些情况下,主要是各种形式和表格。我认为他们可以使用<?><Object><Void>,但我不确定是哪一个。

<?>似乎最适合我,但有很多地方我不能使用通配符。 <Object>适用于所有情况,但它让我感到不安,因为它基本上是在不使用通配符的情况下编写通配符,这对于我的大脑部分本身就是错误的。在Wicket migration guide中建议使用<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的引用应该参数化

     

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

没有错误。

6 个答案:

答案 0 :(得分:3)

如果您不打算使用组件的基础模型对象,请使用 Void

在语义上,它更健全,传达更好这个想法不是模型对象可以是任何东西,而是它在语义上没有任何东西,永远不会被使用。在这种情况下,Void关键字主要用作传统解决方案。

如果您打算使用模型对象而不关心,我认为这不是您的意思,**使用通配符**您可以在哪里不能(构造函数参数等),无论是Void,Object,还是可能是其他“包含”类,根据组件的特定语义和所需的泛型键入行为做出决策(例如,在构造函数的情况下)对于组件变量,您可以考虑构造函数对Void或Object类型的处理方式。)

当然,这就是“良好的编程实践”理论,在实践中你不需要太在意,虽然这种思维可以帮助你的队友维护你的代码,并且可以帮助你更好地理解它,也许甚至可以预测错误。

在wicket用户中使用通配符是相当普遍的,可能比我建议的更常见,但这不是因为通配符是常规,但很可能仅仅是由于大多数代码示例在搜索引擎中弹出更喜欢通配符。尽管如此,正如迁移指南对Void的建议所显示的那样,不仅通配符的语义连贯性较低,而且它们似乎也不是绝对的约定,甚至是挑战,wicket的开发人员看来,我们最好还是假设,充分了解他们的类型的内部运作,以便他们的建议得到认真对待。

答案 1 :(得分:1)

可用的替代方案是:

  1. 只需使用原始类型(如示例代码中所示),只需忽略警告
  2. 即可
  3. 使用通配符/对象通用
  4. 使用extends generic
  5. 我假设您的问题是#1不适合您。

    #2(通配符/对象)

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

    OR

    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实际可能的通用属性。

    • 如果它们是您自己的类 AND ,您实际上想要访问泛型类型的对象,我会选择第3种方法,
    • 否则,例如StringInteger等盒装基元,或者如果你没有使用泛型类型的对象,我会选择方法2.

    HTH

答案 2 :(得分:1)

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

所以经验法则应该是:

  • 如果您只处理遗传对象而不使用其参数化内容,请使用<?>,以便您第一眼看到参数与行为无关。
  • 在任何其他情况下,请使用包含您的方法旨在使用的所有类型的最具体的参数。极端情况为<Object>,其他情况包括<? extends SomeTopLevelType>

答案 3 :(得分:0)

我没有使用过wicket,Vodafone阻止我看到API文档。但是,您似乎缺少许多通用参数,并希望如下:

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

如果你想添加其他IColumn个不相关的泛型参数,你需要这样的东西;

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);

答案 4 :(得分: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));
            }
        });
    }
};

使用泛型可以很容易地判断出这是一个人员列表,其中包含指向编辑页面的链接,该页面使用人物作为模型。不幸的是,在wicket中,您的组件通常不会有与之关联的模型。在这种情况下,getModel()将返回null,因此要使用的正确类型是<Void>,它实际上是null的占位符。

DocumentProcessor

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

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

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

您可以查看

More 1.4 Migration info

Void type parameter

答案 5 :(得分:-1)

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

考虑以下示例:

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

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

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

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

您需要为通用类和接口执行相同的操作。