Home > Software engineering >  How to restrict generic types (question is more difficult than it seems)
How to restrict generic types (question is more difficult than it seems)

Time:09-03

Okay, question is not so simple as it seems from the title.

Let's start from the beginning. Assume there are two collections containing DataRow objects:

public class DataRow<K, V> {
    K key;
    V value;

    public DataRow(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }
}

Example in pseudocode as follows:

c1 = [DataRow(0, "Italy"), DataRow(1, "Germany"), DataRow(2, "France")]
c2 = [DataRow(0, "Rome"), DataRow(1, "Berlin"), DataRow(3, "Budapest")]

It is needed to have 3 different ways of joining these collections in the one collection of JoinedDataRow's (Inner join, Left join, Right join):

public class JoinedDataRow<K, V1, V2> {
    K key;
    V1 value1;
    V2 value2;

    public JoinedDataRow(K key, V1 value1, V2 value2) {
        this.key = key;
        this.value1 = value1;
        this.value2 = value2;
    }

    public K getKey() {
        return key;
    }

    public V1 getValue1() {
        return value1;
    }

    public V2 getValue2() {
        return value2;
    }
}

Operations are similar to the ones in SQL language. Example of result for inner join: [JoinedDataRow(0, "Italy", "Rome"), JoinedDataRow(1, "Germany", "Berlin")]

There is an interface for these implementations of operations called JoinOperation:

public interface JoinOperation<D1, D2, R> {
    Collection<R> join(Collection<D1> leftCollection, Collection<D2> rightCollection);
}

Where D1 and D2 are DataRow's and R is a JoinedDataRow (result)

And there are 3 implementations of the interface: InnerJoin, LeftJoin, RightJoin

There is no need to provide code for all three implementations. Here is just a skeleton for InnerJoin:

public class InnerJoinOperation<D1, D2, R> implements JoinOperation<D1, D2, R> {
    @Override
    public Collection<R> join(Collection<D1> leftCollection, Collection<D2> rightCollection) {
        return null;
    }
}

The problem: Right now it is possible and completely legal to pass to join method collection of Integers, for example.

join() method should look like this to avoid it:

@Override
    public Collection<JoinedDataRow<K, V1, V2>> join(Collection<DataRow<K, V1>> leftCollection, Collection<DataRow<K, V2>> rightCollection) {
        return null;
    }

But it is forbidden to modify the interface JoinOperation, therefore the method above is not valid.

The question: How to restrict type of passed parameters in join method so that it would not be possible to pass anything except DataRow there.

I tried:

public class InnerJoinOperation<D1 extends DataRow, D2 extends DataRow, R extends JoinedDataRow> implements JoinOperation<D1, D2, R> {
    @Override
    public Collection<R> join(Collection<D1> leftCollection, Collection<D2> rightCollection) {
        return null;
    }
}

But here I am using raw DataRow and when I wanna do something like this:

Collection<DataRow<Integer, String>> lCollection = new ArrayList<>(List.of(
                new DataRow<>(0, "Italy"),
                new DataRow<>(1, "Germany"),
                new DataRow<>(2, "France")
        ));

        Collection<DataRow<Integer, String>> rCollection = new LinkedList<>(List.of(
                new DataRow<>(0, "Rome"),
                new DataRow<>(1, "Berlin"),
                new DataRow<>(3, "Budapest")
        ));

        var joiner = new InnerJoinOperation<>();

        var joined = joiner.join(lCollection, rCollection);

expression joiner.join(lCollection, rCollection) show compile time error: enter image description here

CodePudding user response:

It sounds like what you want is just

public class InnerJoinOperation<K, V1, V2>
    implements JoinOperation<DataRow<K, V1>, DataRow<K, V2>, JoinedDataRow<K, V1, V2>> {
  @Override
  public Collection<JoinedDataRow<K, V1, V2>> join(Collection<DataRow<K, V1>> leftCollection, Collection<DataRow<K, V2>> rightCollection) {
    return null;
  }
}
  • Related