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.
Related
Code:
LinkedBinarySearchTree <Pair<String, Integer>> at = new LinkedBinarySearchTree<>();
Pair<String, Integer> p = new Pair<>(str, dni);
at.insert(p);
Pair is a class that has been given to me, it isn't the java class Pair (idk if java has a default pair class but just in case it has one, this one isn't that).
The class pair doesn't have a compareTo defined in it and the method insert uses the compareTo at some point and when it does it crashes.
I need to implement the abstract class Comparable and override the method compareTo in the class from the outside, without modifying the code of the class Pair, which means I have to do it from the "outside".
Is there a way to do this?
This is what I did previously:
public class MyComparator implements Comparator <Pair<String, Integer>> {
#Override
public int compare(Pair<String, Integer> o1, Pair<String, Integer> o2) {
final Collator instance = Collator.getInstance();
instance.setStrength(Collator.NO_DECOMPOSITION);
if (!o1.getFirst().equals(o2.getFirst())){
return o1.getFirst().compareTo(o2.getFirst());
} else {
return o1.getSecond().compareTo(o2.getSecond());
}
}
}
But it doesn't work with Comparator, it has to be Comparable for some reason and I don't know how to do it because I can't refer (this):
public class MyComparable implements Comparable <Pair<String, Integer>> {
#Override
public int compareTo(Pair<String, Integer> o) {
final Collator instance = Collator.getInstance();
instance.setStrength(Collator.NO_DECOMPOSITION);
//I can't use "this" here because ovbiously I'm not inside the class Pair so I don't know how to do it
if (!this.getFirst().equals(o.getFirst())){ //I can't use "this"
return this.getFirst().compareTo(o.getFirst());
} else {
return this.getSecond().compareTo(o.getSecond());
}
}
}
I need help please I've been trying to find an answer by myself and I'm out of ideas... I'm sorry if this question is too easy or unhelpful but I'm kinda struggling here :/.
EDIT:
I debugged the program and this is where it crashes, that's why I
think I need the Comparable:
public class DefaultComparator<E> implements Comparator<E> {
#Override
public int compare(E a, E b) throws ClassCastException {
return ((Comparable<E>) a).compareTo(b); //here
}
}
Could you possibly extend Pair with you own class that also implements Comparable and use that?
public class MyPair<T, O> extends Pair<T, O> implements Comparable<MyPair<T, O>> {
#Override
public int compareTo(MyPair<T, O> other) {
//logic to compare
}
}
and then use that
LinkedBinarySearchTree <MyPair<String, Integer>> at = new LinkedBinarySearchTree<>();
Edit based on comments:
If you know the types of objects used in the Pair are themselves Comparable then you can use bounded generic parameters. So the example above becomes:
public class MyPair<T extends Comparable<T>, O extends Comparable<O>> extends Pair<T, O> implements Comparable<MyPair<T, O>> {
#Override
public int compareTo(MyPair<T, O> other) {
//Now the compiler knows that T and O types are Comparable (that
//is they implement the Comparable interface) and
//this means their compareTo() can be used
return this.getFirst().compareTo(other.getFirst());
}
}
You can create a wrapper class to pair without changing pair but adding comparable to wrapper and after that you need to change your linkedlist's generic to ComparablePair
class ComparablePair implements Comparable < ComparablePair > {
private Pair < String,Integer > pair;
#Override
public int compareTo(ComparablePair o) {
Pair otherPair = o.pair;
//compare this.pair and otherpair here.
return 0;
}
}
LinkedBinarySearchTree <ComparablePair> at = new LinkedBinarySearchTree<>();
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.
I often have a Comparator type while I need a Comparable and the other way around. Is there a reusable JDK API to convert from one another? Something along the lines of:
public static <C> Comparable<C> toComparable(final Comparator<C> comparator) {
// does not compile because Hidden can not extend C,
// but just to illustrate the idea
final class Hidden extends C implements Comparable<C> {
#Override
public int compareTo(C another) {
return comparator.compare((C) this, another);
}
};
return new Hidden();
}
public static <C extends Comparable<C>> Comparator<C> toComparator(final Class<C> comparableClass) {
return new Comparator<C>() {
#Override
public int compare(C first, C second) {
assert comparableClass.equals(first.getClass());
assert comparableClass.equals(second.getClass());
return first.compareTo(second);
}
};
}
ComparableComparator from Apache Commons Collections seems to address Comparable<T> to Comparator problem (unfortunately its not generic type-friendly).
The reverse operation is not quite possible because the Comparator<T> represents algorithm while Comparable<T> represents actual data. You will need composition of some sort. Quick and dirty solution:
class ComparableFromComparator<T> implements Comparable<T> {
private final Comparator<T> comparator;
private final T instance;
public ComparableFromComparator(Comparator<T> comparator, T instance) {
this.comparator = comparator;
this.instance = instance;
}
#Override
public int compareTo(T o) {
return comparator.compare(instance, o);
}
public T getInstance() {
return instance;
}
}
Say you have class Foo that is not Comparable<Foo> but you have Comparator<Foo>. You use it like this:
Comparable<Foo> comparable = new ComparableFromComparator<Foo>(foo, comparator);
As you can see (especially without mixins) it's pretty ugly (and I'm not even sure if it'll work...) Also notice that comparable doesn't extend Foo, you have to call .getInstance() instead.
Since Java 8 the Comparator interface has had a few utility default methods added that assist with deriving a comparator from a comparable.
Consider the following example of sorting users by first name.
class Person {
String firstName;
String lastName;
}
List<Person> people = ...
people.sort(Comparator.comparing(Person::firstName));
You can obtain an instance of Comparator able to compare instance of Comparable type simply with
java.util.Comparator.naturalOrder()
see Comparator.naturalOrder()
this is a sort of conversion from Comparable to Comparator
Comparable items can be sorted as they have a compareTo:
Collection<Comparable> items;
Collections.sort(items);
If items are not Comparable, they need a Comparator object to do the comparison:
Collections<T> items;
Collections.sort(items, comparator);
A bridging Comparator is trivial, and you did it already.
Wrapping every T item with some Comparable adapter having a Comparator, seems useless.
First of all not inheritance but as field one needs to wrap the item.
public class CatorComparable<T> implements Comparable<CatorComparable<T>> {
public T value;
private Comparator<T> cator;
public CatorComparable(T value, Comparator<T> cator) {
this.value = value;
this.cator = cator;
}
#Override
public int compareTo(CatorComparable<T> other) {
return cator.compareTo(value, other.value);
}
}
Too much overhead.
I don't think you can really convert between them, nor does it really make sense to, since Comarable is a property of the class itself, while Comparator is an external class.
The best bet would be to write some sort of utility class that contains the underlying comparison logic (and probably have that implement Comparator), then use that class as a part of the logic for the Comparable implementation on the class itself.
Comparator<? super E> comparator()
This method is declared in the Sorted Set interface.
What does the super mean?
How is the above method different from a Generic Method, and a method with Wildcard arguments.
This means that the type of comparison can be a supertype of the current type.
Eg. you can have the following:
static class A {
}
static class B extends A {
}
public static void main(String[] args) {
Comparator<A> comparator = new Comparator<A>() {
public int compare(A a1, A b2) {
return 0;
}
};
// TreeSet.TreeSet<B>(Comparator<? super B> c)
SortedSet<B> set = new TreeSet<B>(comparator);
// Comparator<? super B> comparator()
set.comparator();
}
In this case, A is a supertype of B.
I hope this has been helpful.
A SortedSet needs to have some rules that it uses to determine the sorting. The Comparator is the implementation of these rules. The interface provides a method to get a reference to it so that you can use it for other purposes, such as creating another set that uses the same sorting rules.
From the javadoc:
"Returns the comparator used to order the elements in this set, or null if this set uses the natural ordering of its elements."
:)
"Super" here means that the method is not required to return a Comparator for E. It might instead return a Comparator for any superclass of E. So, to make that concrete, if E were String, this method could give you a more general Comparator for Object.
A generic method would declare a new generic parameter of its own. This method merely references the generic parameter E which was declared by the class declaration SortedSet<E>. Generic methods are less common. They are usually static, like the Arrays method
public static <T> List<T> asList(T...)
Here, T is declared and used only in this method. It shows that the type of the objects in the returned list is the same as the type of the objects in the vararg parameter.
I'm not sure the exact definition of wild card arguments. ? Is the wild card character. The general pattern when you get a wild card parameter like List<?> is that you can take objects out of it and cast them to Object but you can't put anything in.
The answer to this is in the interface declaration: public interface SortedSet<E> extends Set<E> { ...
This means that any class that implements SortedSet should specify which Type they will be working with. For example
class MyClass implements SortedSet<AnotherClass>
and this will produce (using eclipse), a bunch of methods such as
public Comparator<? super AnotherClass> comparator()
{
return null;
}
public boolean add( AnotherClass ac)
{
return false;
}
Of cause this will work with all sub-classes of AnotherClass as Paul Vargas pointed out.
The other aspect you might be missing is that Comparator is also an interface: public interface Comparator<T>. So what you are returning is an implementation of this.
Just for interest another useful way to use the Comparator interface is to specify it anonymously as part of the Arrays.sort(Object[] a, Comparator c) method:
If we had an Person class we could use this method to sort on age and name like this:
Person[] people = ....;
// Sort by Age
Arrays.sort(people, new Comparator<Person>()
{
public int compare( Person p1, Person p2 )
{
return p1.getAge().compareTo(p2.getAge());
}
});
// Sort by Name
Arrays.sort(people, new Comparator<Person>()
{
public int compare( Person p1, Person p2 )
{
return p1.getName().compareTo(p2.getName());
}
});
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.