1

JAXB has generated a class with a setter method that takes an abstract class as its parameter:

setAppointmentDate(AvailableDates)

I understand that I cannot instantiate abstract classes, so in order to create an AvailableDates object to pass to the method, I've created an anonymous subclass of AvailableDates (which at the same time instantiates it). This works and gives me expected results. However, to create the anonymous subclass, I've hard coded the name of the abstract class:

AvailableDates anonymous = new AvailableDates() {};

I now need to make this dynamic as the name of the class AvailableDates will be passed in as a parameter. I started to use reflection for this:

String className = "AvailableDates";
Class abstractClass = Class.forName(className);

I now need to create an anonymous class from this but can't see how I can. The best I can see is:

Object dates = abstractClass.newInstance();

This will obviously fail as you can't create an instance of an abstract class. I saw Is it possible to create an anonymous class while using reflection? and if I understand correctly, it sounds like I can't use reflection with anonymous classes. Can anyone add more advice - is there another way round this especially in the context of JAXB?

Community
  • 1
  • 1
user3572079
  • 135
  • 1
  • 2
  • 12
  • Do not instantiate an abstract class. First question: why does the nasty evil JAXB expect for an abstract class? – Dávid Horváth Dec 19 '14 at 10:05
  • An anonymous class can only be created with a constructor, which you don't have when using reflection. – Bubletan Dec 19 '14 at 10:09
  • @Dávid Horvath, I'm not sure why the parameter is an abstract class. I just let Eclipse create the JAXB classes from the XSD and this was the result. – user3572079 Dec 19 '14 at 10:30
  • @Bubletan, doesn't sound too good :( Do you know if there is another way other than using an anonymous class? I will still need to use reflection with an abstract class. – user3572079 Dec 19 '14 at 10:37
  • @DávidHorváth - JAXB like any other bit of software expects a concrete subclass as the value, where the property is an abstract class. – bdoughan Dec 19 '14 at 11:40
  • Of course. But must to check why the type is this (learn how to use the generated result of JAXB). – Dávid Horváth Dec 19 '14 at 11:51

1 Answers1

1

Where Did the Abstract Class Come From?

JAXB would generate an abstract class to correspond to an abstract complex type in the XML Schema. In the example below the complex type contactInfo would correspond to an abstract class called ContactInfo, also the generated class Customer would have a property of this type.

<xs:schema 
    version="1.0" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns="http://www.example.com/foo"
    targetNamespace="http://www.example.com/foo">

    <xs:element name="customer">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="contactInfo" type="contactInfo" minOccurs="0"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:complexType name="contactInfo" abstract="true">
        <xs:sequence/>
    </xs:complexType>

    <xs:complexType name="address">
        <xs:complexContent>
            <xs:extension base="contactInfo">
                <xs:sequence>
                    <xs:element name="street" type="xs:string" minOccurs="0"/>
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="phoneNumber">
        <xs:complexContent>
            <xs:extension base="contactInfo">
                <xs:attribute name="number" type="xs:string"/>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

</xs:schema>

How to Set a Property that is an Abstract Type

For a property of this type you set the value to be one of the concrete subclasses.

    Address address = new Address();
    customer.setContactInfo(address);

Or

    PhoneNumber phoneNumber = new PhoneNumber();
    customer.setContactInfo(phoneNumber);

How to Decide Which Subclass to Use?

How do decide which of the concrete subclasses to use?

There isn't really an automated way to do this, you need to make a specific choice based on the data you are trying to represent. In the example above if you want the customer to be contacted by mail you set an instance of Address otherwise you set an instance of PhoneNumber.


UPDATE

I did try using one of the concrete subclasses and it works like you said. The downside is that these concrete subclasses are not really related to the setter method that I'm using. An example I can think of is setAppointmentDate(StaffLeaveDates). Here StaffLeaveDates extends AvailableDates as a concrete subclass but is not really anything to do with an appointment date but it still works. Do you think this is the best I can do? I'm just thinking that it could look confusing.

JAXB is going to enable you to create any XML document that is compatible with your XML Schema. As far as it is concerned you told it (via the XML Schema) to allow any type that extends AvailableDates so it will happily accept StaffLeaveDates. You can try and change your XML Schema so it is more restrictive (see example below).

XML Schema

Below is an updated version of the XML Schema that introduces a new subtype of contactInfo called emailAddress that I don't want to ever be used as a value on the contactInfo property on customer. So I have changed customer to have a choice between address and phoneNumber.

<xs:schema 
    version="1.0" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns="http://www.example.com/foo"
    targetNamespace="http://www.example.com/foo">

    <xs:element name="customer">
        <xs:complexType>
            <xs:choice>
                <xs:element name="address" type="address"/>
                <xs:element name="phoneNumber" type="phoneNumber"/>
            </xs:choice>
        </xs:complexType>
    </xs:element>

    <xs:complexType name="contactInfo" abstract="true">
        <xs:sequence/>
    </xs:complexType>

    <xs:complexType name="address">
        <xs:complexContent>
            <xs:extension base="contactInfo">
                <xs:sequence>
                    <xs:element name="street" type="xs:string" minOccurs="0"/>
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="phoneNumber">
        <xs:complexContent>
            <xs:extension base="contactInfo">
                <xs:attribute name="number" type="xs:string"/>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="emailAddress">
        <xs:complexContent>
            <xs:extension base="contactInfo">
                <xs:attribute name="address" type="xs:string"/>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

</xs:schema>

Generated Model

Now my Customer class can hold onto an Address or PhoneNumber (or both unfortuately), but not an EmailAddress. Whatever property is null won't be marshalled to XML.

@XmlRootElement(name = "customer")
public class Customer {

    protected Address address;
    protected PhoneNumber phoneNumber;
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • thanks Blaise - is there a glimmer of hope here? How do decide which of the concrete subclasses to use? – user3572079 Dec 19 '14 at 12:10
  • @user3572079 - I have updated my answer with some additional information. – bdoughan Dec 19 '14 at 15:47
  • Thank you Blaise for your updated answer - appreciate the detail. I did try using one of the concrete subclasses and it works like you said. The downside is that these concrete subclasses are not really related to the setter method that I'm using. An example I can think of is `setAppointmentDate(StaffLeaveDates)`. Here `StaffLeaveDates` extends `AvailableDates` as a concrete subclass but is not really anything to do with an appointment date but it still works. Do you think this is the best I can do? I'm just thinking that it could look confusing. – user3572079 Dec 19 '14 at 16:21
  • 1
    @user3572079 - I have updated my answer with some information that should help. – bdoughan Dec 19 '14 at 16:50