Может ли JAXB генерировать ArrayList вместо List? - PullRequest
4 голосов
/ 25 января 2011
<complexType name="BookShelf">
   <sequence>
      <choice minOccurs="0" maxOccurs="unbounded">
         <element name="newBook" type="string"/>
         <element name="oldBook" type="string"/>
      </choice>
   </sequence>
</complexType>

JAXB генерирует свойство как List<JAXBElement<String>>.Можно ли сгенерировать его как ArrayList?

Ответы [ 3 ]

14 голосов
/ 25 января 2011

Почему, что вам это даст?

  1. ArrayList<E> не имеет общедоступных методов, отсутствующих в интерфейсе List<E>,таким образом, вы ничего не могли бы сделать с ArrayList<E>, который вы не могли бы сделать с любым другим List<E> (на самом деле есть один: ArrayList.trimToSize(), спасибо @Joachim Sauer, но вряд ли когда-либо понадобится).
  2. Это ужасная практика для API - принимать или возвращать типы реализации вместо базовых интерфейсов.Я бы посоветовал вам следовать Коллекционному следу Учебного пособия по Sun Java и / или прочитать Эффективная Java от Джошуа Блока (вы поймете, о чем он говорит из этого краткого предварительного просмотра , который является источником приведенной ниже цитаты), чтобы узнать больше о платформе Collections и использовании интерфейса.
  3. Кто сказал, что базовая реализация List не ArrayList?В любом случае ArrayList является наиболее часто используемой реализацией 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>();

[...]

Источник: Эффективная Java, предварительный просмотр в SafariBooksOnline .

5 голосов
/ 25 января 2011

По умолчанию свойство будет списком, а базовая реализация - 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;
    }

}

Для получения дополнительной информации:

1 голос
/ 25 января 2011

Вы не можете изменить тот факт, что API генерирует список.

Однако, предполагая, что базовая реализация фактически создает ArrayList, вы всегда можете просто привести его к ArrayList:

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

Или, если это не arraylist (т. Е. Вы получаете исключение, пытающееся выполнить вышеизложенное ...), вы можете сгенерировать новый ArrayList, содержащий те же элементы списка.однако вам не нужно делать ничего из этого: всегда лучше писать код против абстракции интерфейса, а не за конкретным классом, когда это возможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...