Most Java tutorials on "Generics" explain either working with Classes and Methods. That's quite simple.
Putting all together in a more complex example is not trivial. I get a number of compile errors.
Can you help getting this complex example right? It may be useful for others as well. I know also that the BaseObjectData is of course not right.
The interface:
public interface ComparatorBase<T> {
List<Difference> isSame(T otherObject);
}
With Difference:
public record Difference(String id, String left, String right) { }
The base class is (Errors: unknow methods: otherObject.getId() or getObjectValue1()):
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@Builder
public class BaseObjectData<T> implements ComparatorBase<T> {
private String id;
private String objectValue1;
private String objectValue2;
@Override
public List<Difference> isSame(T otherObject) {
List<Difference> differences = new ArrayList<>();
if( ! getObjectValue1().equals( otherObject.getObjectValue1())) {
differences.add( new Difference( getId(), getObjectValue1(), otherObject.getObjectValue1()));
}
if( ! getObjectValue2().equals( otherObject.getObjectValue2())) {
differences.add( new Difference( getId(), getObjectValue2(), otherObject.getObjectValue2()));
}
return differences;
}
T findMatchingObject( List<T> otherObjects) {
for( T object: otherObjects) {
if( object.getId().equals( id)) {
return object;
}
}
return null;
}
}
Then an inherited class: (Error: unknown method: otherObject.getObjectValue3())
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class InheritLevel1Data<T> extends BaseObjectData implements ComparatorBase<T> {
private String objectValue3;
public InheritLevel1Data( String id, String d1, String d2, String d3) {
super( id, d1, d2);
this.objectValue3 = d3;
}
@Override
public List<Difference> isSame(T otherObject) {
List<Difference> differences = super.isSame( otherObject);
if( ! getObjectValue3().equals( otherObject.getObjectValue3())) {
differences.add( new Difference( getId(), getObjectValue3(), otherObject.getObjectValue3()));
}
return differences;
}
}
Test class:
public class RunnerTest {
private static final Logger logger = Logger.getLogger( "My logger");
public static void main(String[] args) {
List<BaseObjectData> objectDataList1 = buildList( "id-1", "data-1", "data-2", "data-3b");
List<BaseObjectData> objectDataList2 = buildList( "id-1", "data-1", "data-2a", "data-3");
List<Difference> differences = new ArrayList<>();
for( BaseObjectData o: objectDataList1) {
BaseObjectData o2 = (BaseObjectData) o.findMatchingObject( objectDataList2);
differences.addAll( o.isSame( o2));
}
differences.forEach( v -> logger.info( "Verschil: " + v));
}
private static List<BaseObjectData> buildList( String id, String data1, String data2, String data3) {
return List.of(
new BaseObjectData( "BO_" + id, data1, data2),
new InheritLevel1Data( "ID1_" + id, data1, data2, data3));
}
}
The output should give the differences: data-2 vs data-2a AND data-3a vs data-3, etc.
@rzwitserloot - and others - thank you for helping! Much appreciated
Primary goal was to perform a deep dive into Generics via a complex inheritance example.
My mistake was that I thought that an inherited method signature with a base-class object parameter would (also) match with an inherited object. That is of course not so! That is a different method.
So, inheritedLevel1Data.isSame( InheritedLevel1Data otherObject) is a completely different method signature than the inherited isSame( BaseObjectData otherObject).
Since working with Java 0.92 I was taken by surprise by Java. Never too old to learn ;-)
ComparatorBase:
public interface ComparatorBase<T> {
List<Difference> isSame(T left, T right);
}
Data level-0:
@NoArgsConstructor
@Setter
@Getter
public class BaseObjectData {
private String id;
private String objectValue1;
private String objectValue2;
private BaseComparator comparatorBase = new BaseComparator();
public BaseObjectData(String id, String objectValue1, String objectValue2) {
this.id = id;
this.objectValue1 = objectValue1;
this.objectValue2 = objectValue2;
}
public List<Difference> isSame(BaseObjectData otherObject) {
if(otherObject != null) {
return comparatorBase.isSame(this, otherObject);
}
return new ArrayList<>();
}
BaseObjectData findMatchingObject( List<BaseObjectData> otherObjects) {
for( BaseObjectData object: otherObjects) {
if( object.getId().equals( id)) {
return object;
}
}
return null;
}
}
Data level-1:
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class InheritLevel1Data extends BaseObjectData {
private String objectValue3;
private InheritLevel1Comparator comparatorBase = new InheritLevel1Comparator();
public InheritLevel1Data(String id, String d1, String d2, String d3) {
super( id, d1, d2);
this.objectValue3 = d3;
}
// Don't use InheritLevel1Data in the signature
public List<Difference> isSame(BaseObjectData otherObject) {
if( otherObject instanceof InheritLevel1Data i1) {
return comparatorBase.isSame(this, i1);
}
return null;
}
}
Data level-2: InheritLevel2Data:
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class InheritLevel2Data extends InheritLevel1Data {
private String objectValue4;
private InheritLevel2Comparator comparatorBase = new InheritLevel2Comparator();
public InheritLevel2Data(String id, String d1, String d2, String d3, String d4) {
super( id, d1, d2,d3);
this.objectValue4 = d4;
}
// Don't use InheritLevel2Data in the signature.
public List<Difference> isSame(BaseObjectData otherObject) {
if( otherObject instanceof InheritLevel2Data i2) {
return comparatorBase.isSame(this, i2);
}
return null;
}
}
Compare level-0:
public class BaseComparator<T extends BaseObjectData> implements ComparatorBase<T> {
public List<Difference> isSame(T left, T right) {
List<Difference> differences = new ArrayList<>();
if( ! left.getObjectValue1().equals( right.getObjectValue1())) {
differences.add( new Difference( left.getId(), left.getObjectValue1(), right.getObjectValue1()));
}
if( ! left.getObjectValue2().equals( right.getObjectValue2())) {
differences.add( new Difference( left.getId(), left.getObjectValue2(), right.getObjectValue2()));
}
return differences;
}
}
Compare level 1:
public class InheritLevel1Comparator<T extends InheritLevel1Data> extends BaseComparator<T> {
public List<Difference> isSame(T left, T right) {
List<Difference> differences = super.isSame(left, right);
if (!left.getObjectValue3().equals(right.getObjectValue3())) {
differences.add(new Difference(left.getId(), left.getObjectValue3(), right.getObjectValue3()));
}
return differences;
}
}
Compare level-2:
public class InheritLevel2Comparator<U extends InheritLevel2Data> extends InheritLevel1Comparator<U> {
public List<Difference> isSame(U left, U right) {
List<Difference> differences = super.isSame( left, right);
if (!left.getObjectValue3().equals(right.getObjectValue3())) {
differences.add(new Difference(left.getId(), left.getObjectValue3(), right.getObjectValue3()));
}
return differences;
}
}
Test runner:
public class RunnerTest {
private static final Logger logger = Logger.getLogger( "My logger");
public static void main(String[] args) {
List<BaseObjectData> objectDataList1 = buildList( "id-1", "data-1", "data-2", "data-3b", "data-4");
List<BaseObjectData> objectDataList2 = buildList( "id-1", "data-1", "data-2a", "data-3", "data-4c");
List<Difference> differences = new ArrayList<>();
for( BaseObjectData o: objectDataList1) {
BaseObjectData o2 = o.findMatchingObject( objectDataList2);
differences.addAll(o.isSame(o2));
}
differences.forEach( v -> logger.info( "Verschil: " + v));
}
private static List<BaseObjectData> buildList( String id, String data1, String data2, String data3, String data4) {
return List.of(
new BaseObjectData( "B0_"+ id, data1, data2),
new InheritLevel1Data( "I1_"+ id, data1, data2, data3),
new InheritLevel2Data( "I2_"+ id, data1, data2, data3, data4));
}
}