0

I want to create DataSets for use in algorithm testing.
I have a generic wrapper class that holds data of type T as well as information about the arbitrary size/complicatedness of the dataset to be able to map input-size to runtime.
Since not everything I want to test on will simply be multiples of something, I do not want to constrain T.
However I'd like to have a constructor that 'recognizes' something as a Collection and can then infer the size itself.

Below code does work, but has a compiler warning for a good reason or needs to throw an Exception respectively.

public class DataSet<T> {
    private final long size;
    private final T data;

    public DataSet(T data, long size) {
        this.data = data;
        this.size = size;
    }

    public <I extends Collection<?>> DataSet(I input) { //allows for DataSet<String> foo = new DataSet(List.<Integer>of(1, 4, 6);
        this.data = (T) input; //compiler warning / ClassCastException: unchecked cast I to T
        this.size = input.size();
    }

    public DataSet (T input) {
        if(!(input instanceof Collection)) {
            throw new IllegalArgumentException("Input needs to be Collection"); //either this or a ClassCastException later
        }
    
        this.data = input;
        this.size = ((Collection<?>) input).size();
    }

    public T getData() {
        return data;
    }

    public long getSize() {
        return size;
    }
}

Both work if used correctly, but allow for mistakes as well, since I'm not sufficiently able to describe to the compiler what I want allowed there ('T is the same as I' or 'T needs to extend Collection in this Constructor only').

So; How do I define a constructor that can put constraints on its Class' type parameter(s) without introducing new type parameters for itself or the class and with compiler support for usage?

1
  • Well, I bit the bullet for now and created a subclass, the type parameter of which needs to extend Collection. Commented Nov 5, 2020 at 14:46

1 Answer 1

2

I think the best way is split you implementation on 2 child, something like:

public interface DataSet<T>{
    T getData();

    long getSize();
}

class SimpleDataSet<T> implements DataSet<T>{
    private final long size;
    private final T data;

    public SimpleDataSet(T data, long size) {
        this.data = data;
        this.size = size;
    }

    public T getData() {
        return data;
    }

    public long getSize() {
        return size;
    }
}

class CollectionDataSet<I> implements DataSet<Collection<I>>{
    private final Collection<I> data;

    public CollectionDataSet(Collection<I> data) {
        this.data = data;
    }

    public Collection<I> getData() {
        return data;
    }

    public long getSize() {
        return data.size();
    }
}

But if cannot do it, you can try to do the following:

  public <I> DataSet (Collection<I> input) {
        this.data = (T) input;
        this.size = input.size();
    }

Or

private final Collection<T> collectionData;
public DataSet (Collection<T> input) {
        this.data = null;
        this.collectionData = input;
        this.size = collectionData.size();
    }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.