【问题标题】:Can JAXB generate ArrayList instead of List?JAXB 可以生成 ArrayList 而不是 List 吗?
【发布时间】:2011-01-25 07:32:26
【问题描述】:
<complexType name="BookShelf">
   <sequence>
      <choice minOccurs="0" maxOccurs="unbounded">
         <element name="newBook" type="string"/>
         <element name="oldBook" type="string"/>
      </choice>
   </sequence>
</complexType>

JAXB 将属性生成为List&lt;JAXBElement&lt;String&gt;&gt;。有什么办法可以生成为ArrayList?

【问题讨论】:

  • 为什么? JAXB 从模式生成一种 API。 API 不应包含具体的类。 ArrayList 比 List 多了一个公共方法:trimToSize(),这对你来说是必不可少的吗?顺便说一句:生成的代码在惰性 getter 中创建了一个 ArrayList 实例。

标签: java collections jaxb arraylist


【解决方案1】:

为什么,这对你有什么好处?

  1. ArrayList&lt;E&gt; 没有公开 不在的方法 List&lt;E&gt;接口,所以有 你无能为力 ArrayList&lt;E&gt; 你做不到 与任何其他List&lt;E&gt;(实际上 有一个: ArrayList.trimToSize(), 谢谢@Joachim Sauer,但它是 几乎不需要)。
  2. API 的做法很糟糕 接受或返回实施 类型而不是底层 接口。我建议你 关注Collections Trail 的 Sun Java 教程和/或阅读 Effective Java by Joshua Bloch (你会知道他是什么 谈论from this short preview,这是下面引用的来源)以了解更多关于 集合框架和接口 用法。
  3. 谁说基础列表 实现不是ArrayListArrayList是最 常用的List实现 无论如何,所以机会很高 JAXB 实际上会返回一个 ArrayList,只是不会告诉你 所以(因为你不需要知道)。

第 52 条:通过接口引用对象(摘录)

第 40 项包含您的建议 应该使用接口而不是 类作为参数类型。更多的 一般来说,你应该赞成使用 接口而不是类 指对象。 如果合适 接口类型存在,那么 参数、返回值、变量、 和字段都应该被声明 使用接口类型。唯一的一次 你真的需要参考 对象的类是你创建的时候 它带有一个构造函数。做这个 具体来说,考虑以下情况 Vector,这是一个实现 List 接口的。进入 打字的习惯:

// Good - uses interface as type
List<Subscriber> subscribers = new Vector<Subscriber>();

而不是这样:

// Bad - uses class as type!
Vector<Subscriber> subscribers = new Vector<Subscriber>();

[...]

来源: Effective Java,preview on SafariBooksOnline

【讨论】:

  • 第 1 项并不完全正确(参见 trimToSize(),Arne 上面提到过)。
  • @Joachim darn,是的,我错过了那个 :-)
  • 和另一个人回答一个问题是不礼貌的。
  • @jonbros 也许,但如果不让 OP 意识到他的问题中的缺陷,就从字面上回答这个问题是不可原谅的
  • @jonbros 不,我没有忘记回答这个问题,我选择不按字面意思回答。其他人确实按字面意思回答,但我选择挑战这个问题。显然,我并不孤单。来吧,回答一些问题,获得一些代表并否决我的答案,但这仍然是回答这个问题的唯一合适方式。
【解决方案2】:

默认情况下,该属性是一个 List,而底层实现是一个 ArrayList。当然,您可以使用 JAXB 自定义来更改底层实现,或者使用您自己的具有 ArrayList 类型属性的类(尽管由于其他答案中提到的原因,这很少是一个好主意)。

默认 JAXB 生成

给定您的 XML 架构:

<schema xmlns="http://www.w3.org/2001/XMLSchema">
   <complexType name="BookShelf">
      <sequence>
         <choice minOccurs="0" maxOccurs="unbounded">
            <element name="newBook" type="string"/>
            <element name="oldBook" type="string"/>
         </choice>
      </sequence>
   </complexType>
</schema>

使用以下命令行:

xjc -d out your-schema.xsd

JAXB 将生成以下类:

package generated;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlType;


@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "BookShelf", propOrder = {
    "newBookOrOldBook"
})
public class BookShelf {

    @XmlElementRefs({
        @XmlElementRef(name = "newBook", type = JAXBElement.class),
        @XmlElementRef(name = "oldBook", type = JAXBElement.class)
    })
    protected List<JAXBElement<String>> newBookOrOldBook;

    public List<JAXBElement<String>> getNewBookOrOldBook() {
        if (newBookOrOldBook == null) {
            newBookOrOldBook = new ArrayList<JAXBElement<String>>();
        }
        return this.newBookOrOldBook;
    }

}

自定义生成

默认情况下,JAXB 的属性类型为 List,底层实现为 ArrayList。如果您希望控制底层实现,您可以使用外部绑定文件,例如:

<jxb:bindings 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    version="2.1">

    <jxb:bindings schemaLocation="f3.xsd">
            <jxb:bindings node="//xs:complexType[@name='BookShelf']/xs:sequence/xs:choice">
                <jxb:property collectionType="java.util.LinkedList"/>
            </jxb:bindings>
    </jxb:bindings>

</jxb:bindings>

还有下面的 XJC 调用:

xjc -d out -b binding.xml your-schema.xsd

改为获取以下类:

package generated;

import java.util.LinkedList;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlType;


@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "BookShelf", propOrder = {
    "newBookOrOldBook"
})
public class BookShelf {

    @XmlElementRefs({
        @XmlElementRef(name = "oldBook", type = JAXBElement.class),
        @XmlElementRef(name = "newBook", type = JAXBElement.class)
    })
    protected List<JAXBElement<String>> newBookOrOldBook = new LinkedList<JAXBElement<String>>();

    public List<JAXBElement<String>> getNewBookOrOldBook() {
        if (newBookOrOldBook == null) {
            newBookOrOldBook = new LinkedList<JAXBElement<String>>();
        }
        return this.newBookOrOldBook;
    }

}

使用你自己的类:

您还可以使用您自己的具有 ArrayList 类型属性的类(尽管由于其他答案中提到的原因,这很少是一个好主意)。

package com.example;

import java.util.ArrayList;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "BookShelf", propOrder = {
    "newBookOrOldBook"
})
public class BookShelf {

    @XmlElementRefs({
        @XmlElementRef(name = "oldBook", type = JAXBElement.class),
        @XmlElementRef(name = "newBook", type = JAXBElement.class)
    })
    protected ArrayList<JAXBElement<String>> newBookOrOldBook ;

    public ArrayList<JAXBElement<String>> getNewBookOrOldBook() {
        if (newBookOrOldBook == null) {
            newBookOrOldBook = new ArrayList<JAXBElement<String>>();
        }
        return this.newBookOrOldBook;
    }

}

更多信息:

【讨论】:

  • 喜欢你的帖子。谢谢。但是,如果您想使用 Sets 而不是 Lists 或任何其他集合类型怎么办?
  • @jonbros - 如果您从 Java 类开始,您可以使用 set 类型或其他集合类型的属性。如果你从 XML 模式开始,我不相信你可以让它生成一个集合。尽管您可以使用 XJC 插件来实现这一点:weblogs.java.net/blog/kohsuke/archive/2005/06/…
  • 是的,从 XML 开始。我去看看插件。我在这里阅读了有关用户定义数据类型的信息:download.oracle.com/docs/cd/E12840_01/wls/docs103/webserv/… 它说 java.utils.Set 的“等效 XML 模式数据类型”是文字数组。但我不知道那是什么意思。谢谢!
【解决方案3】:

您无法更改 API 生成列表这一事实。

但是,假设底层实现实际上生成了一个 ArrayList,您始终可以将其转换为 ArrayList:

ArrayList<JAXBElement<String>> arrayList = 
        (ArrayList<JAXBElement<String>>) list;

或者,如果它不是一个数组列表(即您尝试上述操作时遇到异常......),您可以生成一个包含列表相同元素的新 ArrayList。

ArrayList<JAXBElement<String>> arrayList = 
        new ArrayList<JAXBElement<String>>(list);

但是,一般来说,您不需要这样做:尽可能针对接口抽象而不是底层具体类进行编码总是更好。

【讨论】:

  • 这是一个经典的 +1 / -1 帖子。在技​​术方面,分享这一点很好,但您应该 a) 更加强调这是一种糟糕的做法(也许将 shouldn't need 替换为 shouldn't ) 和 b) 如果没有 instanceof 检查,几乎不应该进行演员表。
  • @Sean Patrick Floyd - 对我来说这是一个明确的 (+1),因为 (1) 它为问题提供了解决方案,并且 (2) 我不知道这样做的实际要求。也许有一个很好的理由。也许不是,但他应该研究你的答案;)
  • 嗨,肖恩:在许多情况下,没有实例检查的 IMO 强制转换是非常好的做法。它只是意味着您知道或愿意对底层的具体类做出假设。如果这个假设被证明是错误的,你会得到一个运行时异常,这正是正确的行为.....它类似于你不需要在每个可能导致的函数调用周围放置一个 try / catch 的方式一个例外。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-05
  • 1970-01-01
相关资源
最近更新 更多