Java - working with list of objects and generics - java

I have a problem with a simple piece of Java code.
I cannot determine if it solves the original purpose;
the guy that wrote it (yet unreachable) just told me that
"an object that implements IA should be a container (List) of IB-like objects”. At first sight I have considered it wrong,
because of the strong constraint (T extends IB<T>) seems illogical,
but the IDE compiler does not show any related error/warning.
If such code is meaningful,
could someone please provide an example of practical usage of such interfaces.
Thanks in advance.
import java.util.List;
public interface IA<T extends IB<T>> {
public List<T> getList();
}
public interface IB<T> {
public T getValue();
}
UPDATE 1: added test with concrete sample classes
class Bar implements IA<Foo>{
List<Foo> list;
#Override
public List<Foo> getList() {
return list;
}
Bar(List<Foo> foos) {
this.list = foos;
}
}
class Foo implements IB<Foo> {
public Float data;
#Override
public Foo getValue() {
return foo;
}
Foo(Float data){
this.data = data;
}
public Float getV() {
return data;
}
}
public class DataTest {
#Test
public void myTest() {
Foo f = new Foo(10F);
List<Foo> fs = new ArrayList<>();
fs.add(f);
Bar bar = new Bar(fs);
List<Foo> foos = bar.getList();
System.out.println(foos.get(0).getV());
}
}
Is this the correct way to use IA and IB?

As T is only used in covariant position, it is safe to use as it is, so the comment on IA can be correct. If IA had a method accepting a T (like int compare(T a, T b)) in one of its parameters, that would cause problems as it were in a contravariant position.

Such a constraint makes sense in certain circumstances. For example, if you want to make an sorted list class, you might do something like
class SortedList<T extends Comparable<? super T>>
where you require that the element type can be compared to itself, which is necessary for you to sort it. (Note that Comparable itself doesn't have a bound on its type parameter, just like here.)
The super in the thing above is because Comparable is a consumer with respect to T, and so per PECS, you should use super wildcards with Comparable. In your case, since IB is a producer with respect to T, you could make it public interface IA<T extends IB<? extends T>> if you want to make it most general.
As to an actual use case that uses this constraint, here's one I came up with that is a class that uses the constraint:
class Bar<T extends IB<T>> implements IA<T> {
T start;
#Override
public List<T> getList() {
List<T> result = new ArrayList<T>();
for (T x = start; x; x = x.getValue()) {
result.add(x);
}
return result;
}
Bar(T start) {
this.start = start;
}
}
Where you have an implementing class that it itself generic (with the same <T extends IB<T>> bound), and it takes one T and generates more Ts until it reaches null, and returns a list of these.
Though this still doesn't require that the interface IA have the constraint, so I guess it still doesn't provide an example where the bound on the parameter of IA is necessary.

Related

Generics more restricted type for constructor

Let's say I will have multiple constructors, other can use T, but one constructor has to use Comparable
I tried to use
public <T extends Comparable<T>>TestT() // does not help
the code
import java.util.ArrayList;
public class TestT<T> {
private ArrayList<T> arr;
public TestT() {
arr = new ArrayList<>();
}
void push(T e){
arr.add(e);
}
}
How to make it so TestT constructor will allow only Comparable? Assuming I have other constructors which do not require Comparable
Your question doesn't make much sense since all constructor have the same name. Try using static methods instead:
public class TestT<T> {
private ArrayList<T> arr;
public static <X extends Comparable<X>> TestT<X> comparable() {
return new TestT<>();
}
private TestT() {
arr = new ArrayList<>();
}
void push(T e){
arr.add(e);
}
}
Please see if the below code answers to your requirements:
class TestT<T> {
private List<T> arr;
// Constructor that receives Comparable
protected <X extends Comparable<? super X>> TestT(X comparableIn1, X comparableIn2) {
/* Locate here your logic that transforms the input Comparable to List<T> */
// some foolish example implementation
if (comparableIn1.compareTo(comparableIn2) > 0) {
arr = new ArrayList<>();
}
else {
arr = new LinkedList<>();
}
}
// Another Constructor that receives a Collection of Comparable
protected <X extends Comparable<? super X>> TestT(Collection<X> comparableCollection) {
/* Locate here your logic that transforms the input Comparable Set to List<T> */
}
// Another Constructor that receives something else
protected TestT(List<T> listOfАrbitraries) {
arr = listOfАrbitraries;
}
void push(T e){
arr.add(e);
}
}
// Create different instances by the above Constructor receiving Comparable
TestT<Integer> arrayListOfIntegers = new TestT<>("1","0");
TestT<Object> linkedListOfObjects = new TestT<>("1","2");
Frankly, the notation <X extends Comparable<? super X>> can be simplified to just <X extends Comparable<X>>. In this case your Constructor argument's type will need to implement compareTo(X o) method, while in a manner I wrote above, the Comparable Interface can be also implemented by a method compareTo(<? super X> o) and the method's definition can be also in one of X's super types.

generic maximum function in Java [duplicate]

I've read awesome "Effective Java" by Joshua Bloch. But one example in the books is left unclear to me. It's taken from chapter about generics, exact item is "Item 28: Use bounded wildcards to increase API flexibility".
In this item it's shown how to write the most universal and bulletproof (at the type system point of view) version of the algorithm of selection maximum element from collection using bounded type parameters and bounded wildcard types.
The final signature of the static method written looks like this:
public static <T extends Comparable<? super T>> T max(List<? extends T> list)
And it's mostly the same as the one of Collections#max function from standard library.
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
I understand why we need bounded wildcard in T extends Comparable<? super T> type constraint, but is it really necessary in type of the argument? It seems to me that it will be the same if we leave just List<T> or Collection<T>, isn't it? I mean something like this:
public static <T extends Comparable<? super T>> T wrongMin(Collection<T> xs)
I've written the following silly example of using both signatures and don't see any diferrence:
public class Algorithms {
public static class ColoredPoint extends Point {
public final Color color;
public ColoredPoint(int x, int y, Color color) {
super(x, y);
this.color = color;
}
#Override
public String toString() {
return String.format("ColoredPoint(x=%d, y=%d, color=%s)", x, y, color);
}
}
public static class Point implements Comparable<Point> {
public final int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public String toString() {
return String.format("Point(x=%d, y=%d)", x, y);
}
#Override
public int compareTo(Point p) {
return x != p.x ? x - p.x : y - p.y;
}
}
public static <T extends Comparable<? super T>> T min(Collection<? extends T> xs) {
Iterator<? extends T> iter = xs.iterator();
if (!iter.hasNext()) {
throw new IllegalArgumentException("Collection is empty");
}
T minElem = iter.next();
while (iter.hasNext()) {
T elem = iter.next();
if (elem.compareTo(minElem) < 0) {
minElem = elem;
}
}
return minElem;
}
public static <T extends Comparable<? super T>> T wrongMin(Collection<T> xs) {
return min(xs);
}
public static void main(String[] args) {
List<ColoredPoint> points = Arrays.asList(
new ColoredPoint(1, 2, Color.BLACK),
new ColoredPoint(0, 2, Color.BLUE),
new ColoredPoint(0, -1, Color.RED)
);
Point p1 = wrongMin(points);
Point p2 = min(points);
System.out.println("Minimum element is " + p1);
}
So can you suggest an example where such simplified signature will be inacceptable?
P.S. And why the heck there is T extends Object in official implementation?
Answer
Well, thanks to #Bohemian I've managed to figure out what's the difference between them.
Consider the following two auxiliary methods
private static void expectsPointOrColoredPoint(Point p) {
System.out.println("Overloaded for Point");
}
private static void expectsPointOrColoredPoint(ColoredPoint p) {
System.out.println("Overloaded for ColoredPoint");
}
Sure, it's not very smart to overload method both for superclass and its subclass, but it let us see what type of return value was actually inferred (points is List<ColoredPoint> as before).
expectsPointOrColoredPoint(min(points)); // print "Overloaded for ColoredPoint"
expectsPointOrColoredPoint(wrongMin(points)); // print "Overloaded for ColoredPoint"
For both methods inferred type was ColoredPoint.
Sometimes you want be explicit about type passed to overloaded function. You may do it a couple of ways:
You can cast:
expectsPointOrColoredPoint((Point) min(points)); // print "Overloaded for Point"
expectsPointOrColoredPoint((Point) wrongMin(points)); // print "Overloaded for Point"
Still no difference...
Or you can tell compiler what type should be inferred using syntax class.<type>method:
expectsPointOrColoredPoint(Algorithms.<Point>min(points)); // print "Overloaded for Point"
expectsPointOrColoredPoint(Algorithms.<Point>wrongMin(points)); // will not compile
Aha! Here is the answer. List<ColoredPoint> can't be passed to function expecting Collection<Point> because generics are not covariant (unlike arrays), but can be passed to function expecting Collection<? extends Point>.
I'm not sure where or who may prefer to use explicit type parameter in such case, but at least it shows where the wrongMin may be inappropriate.
And thanks to #erickson and #tom-hawtin-tackline for answers about purpose of T extends Object constraint.
The difference is in the type returned, especially influenced by inference, whereby the type may be a type hierarchically between the Comparable type and the List type. Let me give an example:
class Top {
}
class Middle extends Top implements Comparable<Top> {
#Override
public int compareTo(Top o) {
//
}
}
class Bottom extends Middle {
}
Using the signature you've provided:
public static <T extends Comparable<? super T>> T max(List<? extends T> list)
we can code this without errors, warnings or (importantly) casts:
List<Bottom> list;
Middle max = max(list); // T inferred to be Middle
And if you need a Middle result, without inference, you can explicitly type the call to Middle:
Comparable<Top> max = MyClass.<Middle>max(list); // No cast
or to pass to a method that accepts Middle (where inference won't work)
someGenericMethodThatExpectsGenericBoundedToMiddle(MyClass.<Middle>max(list));
I don't know if this helps, but to illustrate the types the compiler as allowed/inferred, the signature would look like this (not that this compiles, of course):
public static <Middle extends Comparable<Top>> Middle max(List<Bottom> list)
The difference between
T max(Collection<? extends T> coll)
and
T wrongMax(Collection<T> xs)
is that the return type of the second version is exactly the same as the collection's element type T, while in the first version T can be a super type of the element type.
The second question: the reason for T extends Object makes sure that T is a class and not an interface.
Update: A slightly more "natural" demonstration of the difference: Suppose you define these two methods:
static void happy(ColoredPoint p, Point q) {}
static void happy(Point p, ColoredPoint q) {}
And call the first one them like this:
happy(coloredPoint, min(points));
happy(coloredPoint, wrongMin(points));
The type inference engine could be able to deduce that in the first call the return type of min should be Point and the code would compile. The second call would fail to compile since the call to happy is ambiguous.
Unfortunately the type inference engine isn't powerful enough at least in Java 7, so in reality both calls fail to compile. The difference is that the first call can be fixed by specifying the type parameter as in Algorithms.<Point>min, while fixing the second call would require an explicit cast.
Not an easy one, but i'll try to be as specific as possible:
in T max(Collection<? extends T> coll)
you could pass an argument like this List<Animal> or List<Cat> or List<Dog>,
and in T wrongMax(Collection<T> xs)
where T is Animal you can't pass as an Argument this
List<Dog>, List<Cat> of course in Runtime you could add Cat or Dog objects in List<Animal> but in compilation time you wouldn't be able to pass a subclass of Animal in the Type of the List being passed as an argument in the wrongMax method, in the other hand, in the max method you could. Sorry for my english, i still learning it :), Regards.

Declaring generics

I have a problem with defining generics in static methods and fields.
Suppose I have a simple interface, used by all classes that contains a field of type T called value:
public interface HasValue<T> {
// Getter:
public T value();
// Setter:
public void setValue(T value);
}
If I have an array of object of a type N that implements HasValue<T>, I may have necessity to order this array. One classical way is to compare those N objects using their value field: if T implements the Comparable<T> interface and both arg0 and arg1 are of type N, then arg0.compareTo(arg1) will be equal to arg0.value().compareTo(arg1.value()).
The goal is to create a usable, not time-consuming, possible simple way to obtain the aforementioned situation.
A possibility would be to create a custom Comparator<N> every time I need something similar. That would force me to write code each time: definitly time consuming.
I could create that Comparator<N> directly in the interface. The first try is to create a method:
It needs to be a default method. Part of the code will test if the class T implements the Comparable interface or not, and for that I need an example of the T class: using this.value().getClass() is the fastest way. With a static method I could not use this.
I need to explicitate that the N class implements the interface HasValue<T>, otherwise the computer will not know.
public default <N extends HasValue<T>> Comparator<N> COMPARE_BY_VALUE() throws Exception{
if(Comparable.class.isAssignableFrom(this.value().getClass()))
return new Comparator<N>() {
public int compare(N arg0, N arg1) {
Comparable value0 = (Comparable) arg0.value(),
value1 = (Comparable) arg1.value();
return value0.compareTo(value1);
}
};
else throw new Exception("The class of the value does not implement the interface Comparable.\n");
}
This strategy works... barely. It's clumsy, involves rawtypes, creates the Comparator<N> every time.
Second try: creating a static field.
The strategy is to separate the testing problem from the rest. A default method will do the test: in case of success the method will return a static Comparator, otherwise an exception.
public default <N extends HasValue<T>> Comparator<?> COMPARE_BY_VALUE() throws Exception{
if(Comparable.class.isAssignableFrom(this.value().getClass()))
return COMPARE_BY_VALUE;
else throw new Exception("The class of the value does not implement the interface Comparable.\n");
}
public static Comparator<HasValue> COMPARE_BY_VALUE = new Comparator() {
public int compare(Object arg0, Object arg1) {
Comparable value0 = (Comparable) ((HasValue)arg0).value(),
value1 = (Comparable) ((HasValue)arg1).value();
return value0.compareTo(value1);
}
};
While declaring the static field I (unfortunately) cannot state something like public static <T, N extends HasValue<T>> Comparator<N> COMPARE_BY_VALUE. That forces me to return a Comparator<HasValue>: not what I wanted.
Using wildcards I can obtain something close:
public default <N extends HasValue<T>> Comparator<?> COMPARE_BY_VALUE() throws Exception{
if(Confrontable.class.isAssignableFrom(this.value().getClass()))
return COMPARE_BY_VALUE;
else throw new Exception("The class of the value does not implement the interface Comparable.\n");
}
public static Comparator<? extends HasValue<? extends Comparable<?>>> COMPARE_BY_VALUE
= new Comparator() {
public int compare(Object arg0, Object arg1) {
Comparable value0 = (Confrontable) ((HasValue<?>)arg0).value(), value1 = (Confrontable) ((HasValue<?>)arg1).value();
return value0.compareTo(value1);
}
};
This modification will return (in theory) a Comparator<N> where N extends HasValue<T>, T extends Comparable<U> and U is actually T.
That because every ? in Comparator<? extends HasValue<? extends Comparable<?>>> is interpreted by the JVM as a potential new class: three ? means three new class (N, T and U), and it happens that T implements Comparable<T> - thus U and T are one and the same.
I still have a great amount of rawtypes...
...but at least I have only one Comparator for each N and T.
Now, while the last strategy seems to works, I would like to know if there is a better way to obtain my goal.
My initial idea was to state something like
public static <T extends Comparable<T>, N extends HasValue<T>> Comparator<N> COMPARE_BY_VALUE = new Comparator() {
public int compare(N arg0, N arg1) {
return arg0.value().compareTo(arg1.value());
}
};
and obtain a Comparator<N> without wildcars. This however sends all types of errors. Someone has an idea?
Just do:
static <T extends Comparable<T>> Comparator<HasValue<T>> createValueComparator() {
return new Comparator<HasValue<T>>() {
#Override
public int compare(HasValue<T> o1, HasValue<T> o2) {
return o1.value().compareTo(o2.value());
}
};
}
This reads: for every type T which implements Comparable this method returns comparator which can compare HasValue<T>.
Java might not be able to properly infer types in such convoluted constructs. You might have to add the types explicitly:
Collections.sort(list, Main.<Integer> createValueComparator());
or:
Comparator<HasValue<Integer>> comparator = createValueComparator();
Collections.sort(list, comparator);
Keep in mind that a lot of programmers overuse generics. Usually there is a simpler way to achieve the same - while still maintaining type safety.

Generic instance variable in non-generic class

I'm trying to write a class that has a generic member variable but is not, itself, generic. Specifically, I want to say that I have an List of values of "some type that implements comparable to itself", so that I can call sort on that list... I hope that makes sense.
The end result of what I'm trying to do is to create a class such that I can create an instance of said class with an array of (any given type) and have it generate a string representation for that list. In the real code, I also pass in the class of the types I'm passing in:
String s = new MyClass(Integer.class, 1,2,3).asString();
assertEquals("1 or 2 or 3", s);
String s = new MyClass(String.class, "c", "b", "a").asString();
assertEquals("\"a\" or \"b\" or \"c\"", s);
Originally I didn't even want to pass in the class, I just wanted to pass in the values and have the code examine the resulting array to pick out the class of the values... but that was giving me troubles too.
The following is the code I have, but I can't come up with the right mojo to put for the variable type.
public class MyClass {
// This doesn't work as T isn't defined
final List<T extends Comparable<? super T>> values;
public <T extends Comparable<? super T>> MyClass (T... values) {
this.values = new ArrayList<T>();
for(T item : values) {
this.values.add(item);
}
}
public <T extends Comparable<? super T>> List<T> getSortedLst() {
Collections.sort(this.values);
return this.values;
}
}
error on variable declaration line:
Syntax error on token "extends", , expected
Any help would be very much appreciated.
Edit: updated code to use List instead of array, because I'm not sure it can be done with arrays.
#Mark: From everything I've read, I really want to say "T is a type that is comparable to itself", not just "T is a type that is comparable". That being said, the following code doesn't work either:
public class MyClass {
// This doesn't work
final List<? extends Comparable> values;
public <T extends Comparable> MyClass (T... values) {
this.values = new ArrayList<T>();
for(T item : values) {
this.values.add(item);
}
}
public <T extends Comparable> List<T> getSortedLst() {
Collections.sort(this.values);
return this.values;
}
}
error on add line:
The method add(capture#2-of ? extends Comparable) in the type List<capture#2-of ? extends Comparable> is not applicable for the arguments (T)
error on sort line:
Type mismatch: cannot convert from List<capture#4-of ? extends Comparable> to List<T>
Conclusion:
What it comes down to, it appears, is that Java can't quite handle what I want to do. The problem is because what I'm trying to say is:
I want a list of items that are
comparable against themselves, and I
create the whole list at once from the
data passed in at creation.
However, Java sees that I have that list and can't nail down that all the information for my situation is available at compile time, since I could try to add things to the list later and, due to type erasure, it can't guarantee that safety. It's not really possible to communicate to Java the conditions involved in my situation without applying the generic type to the class.
I think that the simple answer is that you cannot do that. If the type of one of a classes attributes depends on a type parameter, that parameter has to be declared at the class level. And I don't think that it "makes sense" any other way.
If T in your example is not a type parameter of the class, what is it? It cannot be the type parameter of the method, because that type is determined by how the method is called. (If the method is called in different static contexts with different inferred types for T, what is the notional type of T in the context of the attribute declaration?)
So to bring this back to what you are trying to do here, an instance of MyClass will hold elements of some type, and you want to be able to insert and remove elements in a statically typesafe fashion. But at the same time you don't want to be able to say what that type is. So how is the compiler supposed to statically distinguish between a MyClass instance that holds (say) Integer objects and one that holds String objects?
I don't even think you could implement this with explicit dynamic typechecks. (I think that type erasure means that the implementation of the getSortedList() method cannot find out what actual type is bound to its return type.)
No. The real solution is to make MyClass a generic class that declares the type parameter T; e.g.
public class MyClass <T extends Comparable<T>> {
and remove the declaration of the method-level type parameter T from the two methods.
There's plenty of unchecked warnings in this, but in principle it's not necessary to keep the List as anything but something containing things you know are Comparable. You enforce the rules you need to in the constructor, and everything else should be fine. How about something like this:
public class MyClass {
final private List<Comparable> values;
public <T extends Comparable<? super T>>MyClass(T... values){
this.values = new ArrayList<Comparable>();
for(T item : values) {
this.values.add(item);
}
}
public <T extends Comparable<? super T>> List<T> getSortedLst() {
Collections.sort(this.values);
return (List<T>)this.values;
}
}
A quick test using the following shows that for classes that implement Comparable (like Integer and String) MyClass behaves as expected, but will throw a compilation error for classes that do not implement Comparable:
class Junk { }
public static void main(String[] args){
MyClass s = new MyClass(1,2,3);
System.out.println(s.getSortedLst());
MyClass a = new MyClass("c", "a", "b");
System.out.println(a.getSortedLst());
MyClass c = new MyClass(new Junk());
}
I believe the following will achieve what you want (stronger typing of Comparable). This will prevent people adding Comparable objects which are not from your interface to the list and allow multiple implementations.
public class test<T extends ComparableType> {
final List<T> values = new ArrayList<T>();
public test (T... values) {
for(T item : values) {
this.values.add(item);
}
}
public List<T> getSortedLst() {
Collections.sort(this.values);
return Collections.unmodifiableList(this.values);
}
}
public interface ComparableType extends Comparable<ComparableType> {}
public class ConcreteComparableA implements ComparableType {
#Override
public int compareTo(ComparableType o) {
return 0;
}
}
public class ConcreteComparableB implements ComparableType {
#Override
public int compareTo(ComparableType o) {
return 0;
}
}
edit:
I know this may be obvious; but if you do not wish the class to be Generic this solution will also work with:
public class test {
final List<ComparableType> values = new ArrayList<ComparableType>();
public test (ComparableType... values) {
for(ComparableType item : values) {
this.values.add(item);
}
}
public List<ComparableType> getSortedLst() {
Collections.sort(this.values);
return Collections.unmodifiableList(this.values);
}
}
Consider it like this (what I am about to say isn't reality. but it illustrates why you need to do what you need to do):
class Foo<T>
{
private T value;
T getValue() { return value; }
void setValue(T val) {value = val; }
}
// some code that uses the above class
Foo<Integer> iFoo = new Foo<Integer>();
Foo<String> sFoo = new Foo<String>();
iFoo.setValue(5);
sFoo.setValue("Hello");
When this happens the compiler (DOES NOT REALLY DO WHAT I AM ABOUT TO SAY!) generates the following code:
class IntegerFoo
{
private Integer value;
Integer getValue() { return value; }
void setValue(Integer val) {value = val; }
}
class StringFoo
{
private String value;
String getValue() { return value; }
void setValue(String val) {value = val; }
}
// some code that uses the above class
IntegerFoo iFoo = new IntegerFoo();
StringFoo< sFoo = new StringFoo();
iFoo.setValue(5);
sFoo.setValue("Hello");
If you were able to have the instance variables/methods parameterized without parameterizing the class the above thing (WHICH IS NOT REALITY!) wouldn't work.
What you are trying to do should be possible with static methods, but I don't think that is what you want.
Can you explain why you want to do the code you are trying to do? Perhaps we can figure out a better way to do what you want to do that works within the language.
I'd do it this way (I did it as a list or as an array), unless you really need the instance variable/methods:
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class MyClass
{
public static <T extends Comparable<T>> List<T> asSortedList(final T ... vals)
{
final List<T> temp;
temp = new ArrayList<T>(vals.length);
temp.addAll(Arrays.asList(vals));
Collections.sort(temp);
return (Collections.unmodifiableList(temp));
}
public static <T extends Comparable<T>> T[] asSortedArray(final Class<?> clazz,
final T ... vals)
{
final T[] temp;
temp = (T[])Array.newInstance(clazz,
vals.length);
System.arraycopy(vals,
0,
temp,
0,
vals.length);
Arrays.sort(temp);
return (temp);
}
public static void main(final String[] argv)
{
final List<String> list;
final String[] array;
list = MyClass2.asSortedList("c", "a", "b");
System.out.println(list);
array = MyClass2.asSortedArray(String.class, "z", "y", "x");
System.out.println(Arrays.deepToString(array));
}
}
the type constraint you want on the variable can't be expressed directly. you can introduce a new type to bridge the problem.
static class MyList<T extends Comparable<? super T>> extends ArrayList<T>{}
final MyList<?> values;
however, there is no point to be extremely type safe in a private piece of code. Generic is there to help you clarify your types, not to obfuscate them.
public class MyClass<T extends Comparable<? super T>> {
// This doesn't work as T isn't defined
final List<T> values;
public MyClass (T... values) {
this.values = new ArrayList<T>(Arrays.asList(values));
}
public List<T> getSortedLst() {
Collections.sort(this.values);
return this.values;
}
}

Java Class hierarchies with Generics, Comparator and sort error

I've been looking around to see if I find something to help me with my problem, but no luck until now. I've got the following classese:
public interface ISort<T> {
public List<T> sort(List<T> initialList);
}
public abstract class Sort<T> implements ISort<T> {
private Comparator<? super T> comparator;
public Sort(Comparator<? super T> comparator) {
this.comparator = comparator;
}
#Override
public List<T> sort(List<T> initialList) {
ArrayList<T> list = new ArrayList<T>(initialList);
Collections.sort(list, comparator);
return list;
}
}
public abstract class InternalTreeItem<T> {
public abstract String getValue();
}
public class D extends InternalTreeItem<Integer> {
private Integer i;
public D(Integer i) {
this.i = i;
}
#Override
public String getValue() {
return i.toString();
}
public Integer getInteger() {
return i;
}
}
public class DComparator implements Comparator<D> {
#Override
public int compare(D o1, D o2) {
return o1.getInteger() - o2.getInteger();
}
}
public class DSort extends Sort<D> {
public DSort(Comparator<D> comparator) {
super(comparator);
}
public DSort() {
super(new DComparator());
}
}
And the test class:
public class TestClass {
#Test
public void test1() {
List<InternalTreeItem<?>> list= new ArrayList<InternalTreeItem<?>>();
list.add(new D(1));
list.add(new D(10));
list.add(new D(5));
ISort<?> sorter = new DSort();
sorter.sort(list);
}
}
The compiler gives an error at the line
sorter.sort(list);
and states
The method sort(List<capture#2-of ?>)
in the type ISort<capture#2-of ?>
is not applicable for the arguments
(List<InternalTreeItem<?>>)
Ok, after a couple of hours and help from a friend, we realized the problem lies with Collections#sort(List<T> list, Comparator<? super T> c) in the abstract class Sort, as I use a Comparator<? extends T>.
I use generics, as I have 2 models, one model's super class is a generic abstract subclassed by 35 classes, and the second model actually has 2 different super classes, which combined, are subclassed by again 35 classes. These hierarchies are given, there's nothing I can do to modify them.
The model here is very simple, but you get the point. Also, there's a factory, that depending on the type of T, returns one sorter, or another.
Can any one please help and provide a solution for my issue (that is to sort a generic list; the parameter type can be a generic superclass or one of it's subclasses).
Thanks and best regards,
Domi
One way to approach this is to use a wrapper class for the classes that you cannot change.
So in your example you want to order a list of object D, based on an Integer value. By putting your objects in a wrapper and then adding this to the list, you can expose the value you wish to sort the list by.
For example, you could define an interface like:
private interface SortableListItem<T> extends Comparable<SortableListItem<T>> {
public T getValue();
}
Then, create a wrapper class for D:
public class DWrapper implements SortableListItem<Integer> {
private D item;
public DWrapper(D item) {
this.item = item;
}
public Integer getValue() {
return item.getInteger();
}
public int compareTo(SortableListItem<Integer> o) {
return getValue().compareTo(o.getValue());
}
}
From here it is pretty simple to create and sort your list:
D item1= new D(1);
D item2= new D(10);
D item3= new D(5);
DWrapper wrapper1 = new DWrapper(item1);
DWrapper wrapper2= new DWrapper(item2);
DWrapper wrapper3= new DWrapper(item3);
List<SortableListItem<Integer>> sortableList = new ArrayList<SortableListItem<Integer>>();
sortableList.add(wrapper1 );
sortableList.add(wrapper2);
sortableList.add(wrapper3);
Collections.sort(sortableList);
You can of course make the wrapper class accept a more generic object - the key is that each object returns a value (in this case an Integer) that the List can be sorted by.
The variable sorter is of type ISort<?>. It could have, say, an ISort<String> assigned to it. The sort method takes an argument of List<T> where T could be String. Clearly you cannot use List<InternalTreeItem<?>> for List<String>, so fortunately the compiler points out the error.
(Note: It's generally a good idea to keep to coding conventions. No I Hungarian prefixes, or single letter class names.)
Running your code what I can deduce is that you get a compile error since it is not possible to capture the wildcard that you specify in below line of class TestClass:
ISort<?> sorter = new DSort();
As I understand an occurrence of wild card is taken to stand for some unknown type and from your code it is not possible to infer the type (for the compiler).
But looking at the code, the class DSort is not written in a way to take type parameters
and any attempt to pass type parameters during creation of instance of DSort gave the error:
The type DSort is not generic; it cannot be parameterized with arguments
But you mention that you cannot alter the code of the modules (i.e I presume of classes DSort etc).
So one way to fix the error would be to not use generics during creation of instance of ISort.
The below code works and the prints the the sorted output (1,5,10)
List<InternalTreeItem<?>> list= new ArrayList<InternalTreeItem<?>>();
list.add(new D(1));
list.add(new D(10));
list.add(new D(5));
// no generic arguments
ISort sorter = new DSort();
List<InternalTreeItem<?>> sortedList = sorter.sort(list);
for(InternalTreeItem i:sortedList) {
System.out.println(i.getValue());
}
but results in a warning of the form ISort is a raw type. References to generic type ISort should be parameterized. But having code that uses generic and having warning of this form is not a good practice . This warning implies that the compiler cannot give cast-iron guarantee about the implicit casts it does to use generics.
If feasible, I think the better solution would be to see how the modules class can re-designed.

Categories