Using Java generics in an interface to enforce implementation of a method with the implementing type as a parameter

15,867

Solution 1

If you just need to handle copyFrom differently if the DataObject it's given is the same type as the object itself, just do something like this:

public class DataObjectImpl implements DataObject {
  public void copyFrom(final DataObject source) {
    if (source instanceof DataObjectImpl) {
      ...
    }
    else {
      ...
    }
  }
}

On the other hand, you could do it like this, using a different name for the method taking an implementation type. However, I don't see what good this does.

public interface DataObject<T extends DataObject<T>> {
  public void copyFrom(DataObject source);
  public void copyFromImpl(T source);
}

public class DataObjectImpl implements DataObject<DataObjectImpl> {
  public void copyFrom(final DataObject source) { ... }
  public void copyFromImpl(final DataObjectImpl source) { ... }
}

Solution 2

You could reduce your code by simply doing:

interface DataObject {
   public <T> void copyFrom(T impl);
}

or to be more succinct:

interface DataObject {
   public <T extends DataObject> void copyFrom(T impl);
}

You could use this by calling:

o.copyFrom(new DataObjectImpl());
o.<DataObjectImpl> copyFrom(new DataObjectImpl());

Using generics, the implementation of DataObject is always going to be erased at runtime. It seems counter intuitive to know about the implementation because you should be programming to the interface. If I were you, I might look into Data Transfer Objects or cloning.

Like Josh Bloch says, use interfaces only to define types.

If your type was defined as:

interface DataObject {
   byte[] getData();
   Long getId();
   Timestamp getCreateDtTm();
}

Your impl might contain:

class DataObjectImpl implements DataObject {
   // member vars
   public DataObjectImpl(DataObject dataObject) {
      this.data = dataObject.getData();
      this.id = dataObject.getId();
      this.createDtTm = dataObject.getCreateDtTm();
   }
   //getters and setters
}

The implementation or copy strategy wouldn't matter because any implementation abides by the type contract and can be reduced to this clearly defined type.

Share:
15,867
Ioeth
Author by

Ioeth

Software Engineer

Updated on June 04, 2022

Comments

  • Ioeth
    Ioeth almost 2 years

    I have an interface like this:

    public interface DataObject {
        ...
        public void copyFrom(DataObject source);
        ...
    }
    

    And a class that implements it:

    public class DataObjectImpl implements DataObject {
        ...
        @Override
        public void copyFrom(final DataObject source) {...}
    
        public void copyFrom(final DataObjectImpl source) {...}
        ...
    }
    

    Is there any way that I can enforce the implementation of a "public void copyFrom(DataObjectImpl source)" method in the DataObject interface, using generics or otherwise?

  • Ioeth
    Ioeth over 13 years
    "public void copyFrom(T source);" has the same type erasure as "public void copyFrom(DataObject source);". I need the interface to specify both.
  • ColinD
    ColinD over 13 years
    @loeth: Why do you need that? If you need to handle things differently when the copyFrom parameter is a DataObjectImpl vs. when it's some other implementation of DataObject, just use the copyFrom(DataObject source) signature and do an instanceof check in the implementation.
  • Ioeth
    Ioeth over 13 years
    I need to do this because currently, all classes implementing the interface do of course specify "public void copyFrom(DataObject source)", but our goal is to move towards the "public void copyFrom(DataObjectImpl source)" methods. However, in the meantime we have hundreds of objects that we don't wish to remove the DataObject method from until the DataObjectImpl method has been specified in the interface for at least one version.