0

I'm working with the following jaxb class hierarchy:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(...)
public class RecordModifyType extends RecordBaseType
{
    ...
    public List<FieldModifyType> getField() { ... }
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(...)
public class RecordAddType extends RecordBaseType
{
    ...
    public List<FieldAddType> getField() { ... }
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(...)
public class FieldModifyType extends FieldBaseType
{
    ...
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(...)
public class FieldAddType extends FieldBaseType
{
    ...
}

I stumbled upon a method in some other class with the following signature (please note the raw types):

private void createFields(FTPPartner user, 
                          String addressMasterDataID, 
                          String connectionMasterDataID, 
                          CRTypeEnum crType, 
                          List addrFields, 
                          List connFields)

This method is called from different parts of the class sometimes with List<FieldAddType> as arguments and other times with List<FieldModifyType>.

Now I'm trying to get rid of the raw types in its signature.

Following the get-put principle, I decided to change the signature to

 private void createFields(FTPPartner user, 
                              String addressMasterDataID, 
                              String connectionMasterDataID, 
                              CRTypeEnum crType, 
                              List<? super FieldBaseType> addrFields, 
                              List<? super FieldBaseType> connFields)
 {
     ...
     FieldBaseType someField = new buildField(...);
     addrFields.add(someField);
     ...
 }

... since the createFields implementation is only putting stuff in these collections. The problem is that since RecordModifyType#getFields() returns List<FieldModifyType> and RecordAddType#getFields() returns List<FieldAddType>, I now can not invoke the method, since it only allows for super types of FieldBaseType, not subtypes:

RecordAddType addrRecord = getRecordAddType(...);
RecordAddType connRecord = getRecordAddType(...);
List<FieldAddType> addrFields = addrRecord.getFields();
List<FieldAddType> connFields = connRecord.getFields();
createFields(user, 
             addressMasterDataID,
             connectionMasterDataID,
             crType
             addFields, // This won't compile
             connFields); // This won't compile
marshalRecord(addrRecord);
marshalRecord(connRecord);

Using raw types is working as expected - there are no compile errors and marshalling works.

So my question is - is there a way to preserve the behaviour but get rid of the raw types?

Community
  • 1
  • 1
mdzh
  • 1,030
  • 2
  • 17
  • 34
  • You may want to create a small and complete example that reproduces the problem. It is unclear what the link is between the createFields method and the getField methods... – assylias Feb 09 '16 at 14:11
  • The problem is the api, you cannot properly use a `List` where you put in a `FieldBaseType` using rawtypes, it will `ClassCastException`s when you retrieve it in the other class. The other solution would be creating a new arraylist to hold the `FieldBaseType` from the list returned by `getFields()` – Ferrybig Feb 09 '16 at 14:17
  • How would `createFields()` decide whether to add `FieldAddType` or `FieldModifyType` instances? – Thomas Feb 09 '16 at 14:17
  • I'm not sure why you have that class hierarchy but it indicates that new records would be of type `RecordAddType` and _only_ have fields of type `FieldAddType` while already existing records would be of type `RecordModifyType` and can have _both_ field types. If that's the case then to me `createFields()` looks like it should only accept `List` since you'd not _create_ modified fields. – Thomas Feb 09 '16 at 14:21

1 Answers1

0

If I understand your problem correctly, the compiler is complaining because it doesn't know that the type ? super FieldBaseType for the call is the the same type for each parameter. Also, because both concrete types are subclasses of FieldBaseType, that's how the type needs to be bound IMHO.

Try typing the method:

private <T extends FieldBaseType> void createFields(FTPPartner user, 
                          String addressMasterDataID, 
                          String connectionMasterDataID, 
                          CRTypeEnum crType, 
                          List<T> addrFields, 
                          List<T> connFields) {
    // during the call, the compiler knows that addrFields and connFields
    // hold the same type as each other
}

Now the type is inferred at the call point.

Bohemian
  • 412,405
  • 93
  • 575
  • 722