The basic idea is that I have an interface that transforms data into different data, and I want to use this interface to transform a specific subset of data.
public static interface Data<D> {
// transform the data into different data
<T> Data<T> transform(Function<D,T> transformer);
}
// "Something" that has been labeled
public static interface Labeled {
String getLabel();
}
// class intended to use the Data<D> interface to transform labeled data specifically
public static class LabeledData<L extends Labeled> implements Data<L> {
public final L labeledData;
public LabeledData(L labeledData) {
this.labeledData= labeledData;
}
// of course this will not compile because it does not override
// <T> Data<T> transform(Function<D,T> transformer);
@Override
public <T extends Labeled> LabeledData<T> transform(Function<L,T> transformer) {
return new LabeledData<>(transformer.apply(labeledData));
}
}
Is there anyway to either redeclare Data.transform or rearrange LabeledData to acomplished the desired functionality and flow?
I understand I "could" create LabeledData.transformLabled(...) and define the original transform to somehow cast to LabledData or throw an exception, but that doesn't sound like a very good solution.
I could also pass in a T LabeledData<L extends Labeled, T extends Labeled>
but I think that would limit the usage to one trasnform type at a time, unless there is some creative way to do it.
CodePudding user response:
You can add an extra type paramter to Data
:
public interface Data<TData, TBound> {
<TTarget extends TBound> Data<TTarget, TBound> transform(Function<TData, TTarget> transformer);
}
TBound
is used as the bound for the method's type parameter, TTarget
.
Now the label data class' transform
would override the interface method:
public static class LabeledData<TLabel extends Labeled> implements Data<TLabel, Labeled> {
public final TLabel labeledData;
public LabeledData(TLabel labeledData) {
this.labeledData= labeledData;
}
@Override
public <TTarget extends Labeled> LabeledData<TTarget> transform(Function<TLabel, TTarget> transformer) {
return new LabeledData<>(transformer.apply(labeledData));
}
}
Notice that if you have a bunch of Data
with different TBound
types in a list, you cannot apply the same transformation to each of them using e.g. map
:
// assume that data1, data2, data3 all have different TBounds
List<Data<String, ?>> list = List.of(data1, data2, data3);
list.stream.map(x -> x.transform(y -> f(y))).toList();
Whatever f(y)
returns, this is not type safe to do.