I have an enum named Partitioning. I want each member of Partitioning to have its own partitioning algorithm. For example, the following would invoke the partitioning algorithm for BLAH:
Partitioning.BLAH.partition(myData, value);
The problem is that the objects I am manipulating all use generics, and I cannot get the generics to work correctly.
These are the classes involved.
public class MyData<T extends Comparable<? super T>> implements Comparable<MyData<T>> {
public List<MyData<T>> partition(T value, Partitioning partitioning) {
return partitioning.partition(this, value);
}
private interface PartitioningAlgorithm<U extends Comparable<? super U>> {
List<MyData<U>> partition(MyData<U> myData, U value);
}
private static class MyAlgorithm<U extends Comparable<? super U>> implements PartitioningAlgorithm<U> {
public MyAlgorithm() {
// Nothing to do
}
@Override
public List<MyData<U>> partition(MyData<U> myData, U value) {
return null; // TODO: Stubbed out for now
}
}
public enum Partitioning {
BLAH(new MyAlgorithm());
private final PartitioningAlgorithm<? extends Comparable<?>> algorithm;
<U extends Comparable<? super U>> Partitioning(PartitioningAlgorithm<U> algorithm) {
this.algorithm = algorithm;
}
private <U extends Comparable<? super U>> List<MyData<U>> partition(MyData<U> myData, U value) {
return algorithm.partition(myData, value);
}
}
}
In the enum's partition method definition, for the line:
return algorithm.partition(myData, value);
I get the following compile error:
java: incompatible types: MyData<U> cannot be converted to MyData<capture#1 of ? extends java.lang.Comparable<?>>
I assume that value
will give a similar error.
Any help would be greatly appreciated.
If all the partitioning algorithms associated with each enum constant is a "general" partitioning algorithm that can partition every type of object, PartioningAlgorithm
should not be generic. Instead, make its partition
method generic.
private interface PartitioningAlgorithm {
<U extends Comparable<? super U>> List<MyData<U>> partition(MyData<U> myData, U value);
}
private static class MyAlgorithm implements PartitioningAlgorithm {
public MyAlgorithm() {
// Nothing to do
}
@Override
public <U extends Comparable<? super U>> List<MyData<U>> partition(MyData<U> myData, U value) {
return null; // TODO: Stubbed out for now
}
}
public enum Partitioning {
BLAH(new MyAlgorithm());
private final PartitioningAlgorithm algorithm;
Partitioning(PartitioningAlgorithm algorithm) {
this.algorithm = algorithm;
}
private <U extends Comparable<? super U>> List<MyData<U>> partition(MyData<U> myData, U value) {
return algorithm.partition(myData, value);
}
}
If some partitioning algorithms can only partition a specific type of object, then this design is inherently not type-safe. The best you can do is to throw an exception at runtime when a partitioning algorithm is used to partition something that it cannot partition.
Add a canPartition
method to PartitioningAlgorithm
:
private interface PartitioningAlgorithm {
<U extends Comparable<? super U>> List<MyData<U>> partition(MyData<U> myData, U value);
boolean canPartition(Class<?> type);
}
private static class MyAlgorithm implements PartitioningAlgorithm {
public MyAlgorithm() {
// Nothing to do
}
@Override
public <U extends Comparable<? super U>> List<MyData<U>> partition(MyData<U> myData, U value) {
return null; // TODO: Stubbed out for now
}
@Override
public boolean canPartition(Class<?> type) {
return true; // can partition everything
}
}
Here is an example of an implementation that only partitions strings:
private static class OnlyPartitionStrings implements PartitioningAlgorithm {
@Override
public <U extends Comparable<? super U>> List<MyData<U>> partition(MyData<U> myData, U value) {
return null; // TODO: Stubbed out for now
// here ytou should cast to String whenever needed
}
@Override
public boolean canPartition(Class<?> type) {
return String.class.isAssignableFrom(type);
}
}
In the enum, you would first check canPartition
and potentially throw an exception.
private <U extends Comparable<? super U>> List<MyData<U>> partition(MyData<U> myData, U value) {
if (algorithm.canPartition(value.getClass())) {
throw new UnsupportedOperationException(algorithm + " cannot partition " + value.getClass());
}
return algorithm.partition(myData, value);
}