I've seen a few questions about the sort for collections having errors in Java. The error I am showing is this:
The method sort(List<T>) in the type Collections is not applicable for the arguments (ArrayList<Time>)
I have imported java.util.Collections and ArrayList. I also imported the class I am calling from. Here is my code:
In the class being called from:
private ArrayList<Time> times;
...
public ArrayList<Time> getTimes() {
return this.times;
}
In the class I am calling the array list to:
public class TimeTUI {
private Scanner scan;
private TimeManager timeManager;
...
private ArrayList<Time> getSortedTimes() {
ArrayList<Time> sortedTimes = this.timeManager.getTimes();
Collections.sort(sortedTimes);
return sortedTimes;
}
The error is appearing on the line showing:
Collections.sort(sortedTimes);
The class Time has to be a Comparable.
Collections.sort(List) expects that the class T implements Comparable interface. If you have used many of the inbuilt classes, you wouldn't find problem, but for the custom classes sort doesn't know how to sort them. So, by implementing Comparable interface, you give definition to a method compareTo.
public class Time implements Comparable {
public int compareTo(Object o) {
// provide your logic of how to sort Time objects.
}
}
Your class type in the List or ArrayList must implement the Interface comparable and override properly the compareTo(...) method,
Is you break this contract and dont implement the interface. the Class Collections has not a valid criteria/rule to compare/sort your list, and therefore your compiler will complain...
I don't think that it is the ArrayList that is the issue here. For example:
ArrayList<String> names = new ArrayList<>();
...
Collections.sort(names);
works just fine.
The content of the list must be comparable so that the sort can work. In this case the Time class and any sub-type must implement Comparable.
Related
Given the following code :
public abstract class Participant {
private String fullName;
public Participant(String newFullName) {
this.fullName = new String(newFullName);
}
// some more code
}
public class Player extends Participant implements Comparable <Player> {
private int scoredGoals;
public Player(String newFullName, int scored) {
super(newFullName);
this.scoredGoals = scored;
}
public int compareTo (Player otherPlayer) {
Integer _scoredGoals = new Integer(this.scoredGoals);
return _scoredGoals.compareTo(otherPlayer.getPlayerGoals());
}
// more irrelevant code
}
public class Goalkeeper extends Player implements Comparable <Goalkeeper> {
private int missedGoals;
public Goalkeeper(String newFullName) {
super(newFullName,0);
missedGoals = 0;
}
public int compareTo (Goalkeeper otherGoalkeeper) {
Integer _missedGoals = new Integer(this.missedGoals);
return _missedGoals.compareTo(otherGoalkeeper.getMissedGoals());
}
// more code
}
The problem is that Goalkeeper won't complie.
When I try to compile that code the Eclipse throws:
The interface Comparable cannot be implemented more than once with
different arguments: Comparable<Player> and Comparable<Goalkeeper>
I'm not trying to compare with Player, but with Goalkeeper, and only with him.
What am I doing wrong ?
The problem is described in Angelika Langer's Generics FAQ #401:
Can a class implement different instantiations of the same generic
interface?
No, a type must not directly or indirectly derive from
two different instantiations of the same generic interface.
The reason
for this restriction is the translation by type erasure. After type
erasure the different instantiations of the same generic interface
collapse to the same raw type. At runtime there is no distinction
between the different instantiations any longer.
(I highly recommend checking out the whole description of the problem: it's more interesting than what I've quoted.)
In order to work around this restriction, you can try the following:
public class Player<E extends Player> extends Participant implements Comparable<E> {
// ...
public int compareTo(E otherPlayer) {
Integer _scoredGoals = this.scoredGoals;
return _scoredGoals.compareTo(otherPlayer.getPlayerGoals());
}
// ...
}
public class Goalkeeper extends Player<Goalkeeper> {
// ...
#Override
public int compareTo(Goalkeeper otherGoalkeeper) {
Integer _missedGoals = this.missedGoals;
return _missedGoals.compareTo(otherGoalkeeper.getMissedGoals());
}
// ...
}
As far as the logic of your design goes, you are not doing anything wrong. However, Java has a limitation that prevents you from implementing the same generic interface with different type parameters, which is due to the way it implements generics (through type erasure).
In your code, Goalkeeper inherits from Player its implementation of Comparable <Player>, and tries to add a Comparable <Goalkeeper> of its own; this is not allowed.
The simplest way to address this limitation is to override Comparable <Player> in the Goalkeeper, cast the player passed in to Goalkeeper, and compare it to this goalkeeper.
Edit
public int compareTo (Player otherPlayer) {
Goalkeeper otherGoalkeeper = (Goalkeeper)otherPlayer;
Integer _missedGoals = new Integer(this.missedGoals);
return _missedGoals.compareTo(otherGoalkeeper.getMissedGoals());
}
I would like to add two points to the existing good answers.
There are reasons why you might not want the design you tried even if it had been possible. It is a bit fluffy.
There are other possible solutions in addition to the one that Sergey Kalinichenko presents.
Your design has downsides
As you know, your design isn’t possible with Java. It’s a restriction with Java generics. Let’s for a moment play what if it had been possible. It would imply some behaviour that I think many would find surprising and/or confusing.
With you design, assume we have:
Goalkeeper goalkeeper1 = new Goalkeeper("Imene");
Goalkeeper goalkeeper2 = new Goalkeeper("Sofia");
Player goalkeeper3 = new Goalkeeper("Maryam");
goalkeeper1.compareTo(goalkeeper2); // would call Goalkeeper.compareTo(Goalkeeper)
goalkeeper1.compareTo(goalkeeper3); // would call Player.compareTo(Player)
We can take it one step further:
List<? extends Player> list1 = new ArrayList<Goalkeeper>();
List<? extends Player> list2 = new ArrayList<Player>();
Now we fill both lists with goalkeepers (only) and sort them. Now list1 should be sorted using Goalkeeper.compsreTo() and list2 probably using Player.compareTo(). It’s beginning to be confusing, isn’t it? Would you want such a design? Would you prefer one where you are more explicit about which way to compare is used when? (Yes, I know, you cannot fill the lists through the variables list1 and list2. You would have to fill the lists before assigning them to those two variables.)
A couple of solutions
Solution 1: Instead of one of your compareTo methods (or both of them) use a Comparator. Either a Comparator<Player> or a Comparator<Goalkeeper> or one of each. For example:
Comparator<Player> playerComparator = Comparator.comparingInt(Player::getScoredGoals);
Solution 2: Introduce a separate class for players that are not goalkeepers. I am calling it FieldPlayer for now for lack of a better word. Both FieldPlayer and Goalkeeper should be subclasses of Player. FieldPlayer implements Comparable<FieldPlayer> and Goalkeeper already implements Comparable<Goalkeeper>. Now Player doesn’t need to implement Comparable, and the conflict is avoided.
So I'm taking a Java class, and one of the assignments we were given involves abstract data types (ADTs). In this assignment, we're supposed to implement an ADT known as Stack through a class called LinkedStack. LinkedStack has one constructor, but in the test cases my professor provided us, a new LinkedStack object can create either a new LinkedList object or a new ArrayList object. My issue with this is that no matter how I define my argument, I still get an error saying that the argument is incompatible with the classes.
I've tried a logical test to see whether the argument was called as a LinkedList or an ArrayList, which I think is a good thing, but I can't figure out how to properly assign the argument.
I've tried setting the argument to a Stack and then casting to a LinkedStack, with a private final variable being of of type "Stack", I've also tried calling the argument as a List, but I can't seem to get rid of the errors preventing me from starting the compiling process.
This is what we start out with:
interface Stack {
public void push(Object d){
}
public Object pop(){
}
public Object peek(){
}
public boolean isEmpty(){
}
}
public class ListStack implements Stack{
public ListStack(/*argument*/){
}
}
//Separate test case file
//example of the test cases
public void peekTest1() {
Stack q = new ListStack(new LinkedList());
// assertion cases follow
}
public void peekTest2() {
Stack q = new ListStack(new ArrayList());
// assertion cases follow
}
If you look for a type that you can use for /*argument*/, you can do it like this:
public class ListStack implements Stack {
public ListStack(List list) {
}
/* note that you must implement all methods from the interface */
}
Why use type List? List is the common interface, implemented by LinkedList and ArrayList. So you can use one of them in the constructor.
Note: You should not use raw types. List and the classes that implement this interface have a type parameter. When possible you should rather use something like List<String> or List<T>. But maybe, you will learn this in a later lesson.
The structure is like this:
public interface ItemList{ }
public enum ItemList1 implements ItemList {
Apple,
Orange;
}
public enum ItemList2 implements ItemList {
Banana,
Grapes;
}
and 5 more such enums
The requirement is to use these enums as Keys in a Map, and in those Maps I have put key as:
public Class SomeClass {
private Map<ItemList, OtherObject> objectList;
//other code
}
The ItemList which goes into the map is decided on runtime. And I need to use a sorted Map like TreeMap for other operations.
So, the TreeMap is unable to compare the key enums obviously because I have declared them as ItemList supertype.
So, I searched other questions and did something like this so that enums could use their compareTo method:
public interface ItemList<SelfType extends ItemList<SelfType>> extends Comparable<SelfType>{ }
public enum ItemList1 implements ItemList<SelfType> {
//enum values
}
But this doesn't solve the problem. I am still getting the same "ClassCastException" when I tried to retrieve a TreeMap which had my enums as Keys.
Please suggest if I am doing anything wrongly here, or what can be an other way to solve this purpose?
EDIT:
Link to the solution which I followed, but it's not working:
How to implement an interface with an enum, where the interface extends Comparable?
EDIT 2
Problem identified. Sorry guys.
My map was getting populated with different types of enums as keys, when all the keys should belong to same type for sorting to work.
Well, you already found out that it was the actual content of the Map, not the declaration of the classes that caused the exception, but it’s worth noting that using collections comparing these enums is easier than you think.
E.g. TreeMap doesn’t care whether its declared Generic type has a comparable key or not. If you have class declarations like
public interface ItemList{ }
public enum ItemList1 implements ItemList { Apple, Orange }
public enum ItemList2 implements ItemList { Banana, Grapes }
you can simply use it as
TreeMap<ItemList,Object> tm=new TreeMap<>();
tm.put(ItemList1.Apple, "A");
tm.put(ItemList1.Orange, "O");
System.out.println(tm.get(ItemList1.Apple)+" is for "+ItemList1.Apple);
tm.clear();
tm.put(ItemList2.Banana, "B");
tm.put(ItemList2.Grapes, "G");
System.out.println(tm.get(ItemList2.Banana)+" is for "+ItemList2.Banana);
without problems; it even works if you declare the map as TreeMap<Object,Object>.
Note that some methods require comparable types when requesting natural order by not specifying a Comparator, e.g. with the type declarations above,
List<Object> list=Arrays.<Object>asList(ItemList1.Orange,ItemList1.Apple,ItemList1.Orange);
Collections.sort(list);
does not compile, however, you can easily circumvent it by requesting the natural order via a null Comparator:
Collections.sort(list, null); // compiles and works
So it’s not necessary to mess around with complicated type declarations like ItemList<SelfType extends ItemList<SelfType>> in most cases.
It sounds like what you should be doing is having
private Map<? extends ItemList, OtherObject> objectList;
...to indicate objectList is a specific map of some subtype of ItemList, and then
objectList = new TreeMap<ItemList1, OtherObject>();
or
objectList = new TreeMap<ItemList2, OtherObject>();
to declare it as some specific type of ItemList. (You may have to store it temporarily as a Map<ItemListN, OtherObject> while you populate it, but then you can put it in objectList.
I have no particular use for this in mind, but is it possible to write a method that accepts any number of nested lists in Java?
I got as far as this:
private <T extends List<? extends T>> void masterOfLists(final T list) {
}
The small issue with this now is that it never ends. I neither want to lose generics, so simply accepting an Object and try casting it to a List every pass is not an option in my question.
I hoped it would be clear enough, but appereantly it isn't for some, I want the method masterOfLists to accept the following examples (and way more):
masterOfLists(new ArrayList<Object>())
masterOfLists(new ArrayList<List<Object>>())
masterOfLists(new ArrayList<List<List<Object>>>())
masterOfLists(new ArrayList<List<List<List<Object>>>>())
Instead of Object it may also be a concrete type like String.
The used List may be any type of list, like ArrayList or LinkedList or your custom implementation.
Using pure List won't help you here, you need to define a recursive class.
As an additional source of inspiration you can take a look at my code for Recursive Tic-Tac-Toe
You could create a class something like this:
public class Recursive<T> {
List<Recursive<T>> sub;
T value;
boolean hasSub() {
return sub != null;
}
T getValue() {
return value;
}
void forEach(Consumer<T> t) {
if (hasSub())
sub.forEach(t);
else t.accept(value);
}
}
You can use logic in this class to prevent it from both having a sub-list and an actual value, using constructors and/or setters.
And then if you want to iterate over it and print out all the sub-items recursively, you can use
Recursive<T> recursive;
recursive.forEach(System.out::println);
Then your method can look like this:
private <T> void masterOfLists(final Recursive<T> list) {
You won't get anywhere using pure Lists because the generic type of the list is not available at runtime, and the generics will only create a mess for you here. Using a recursive class is much easier.
The 'cheap' solution is to extend the ArrayList class with your own name, and force the Generics on the subclass. The SubClass is still an ArrayList....:
public class NestingList extends ArrayList<NestingList> {
// all we do is set the Generics...
}
So I have a class that implements Comparable (I have a dummy method here for brevity)
public class MarkovEntry<T extends Chainable> implements Comparable<MarkovEntry<T>>
{
// Compare two rows by ID
public int compareTo(MarkovEntry<T> e)
{
return 0;
}
}
And a method in another class that takes a Comparable (once again, dummy method)
public class ArrayOps
{
public static int binSearch(ArrayList<Comparable> list, Comparable c)
{
return 0;
}
}
Yet when I try to call my method as follows
int index = ArrayOps.binSearch(entries, newEntry);
Where entries is an ArrayList of MarkovEntry's and newEntry is a MarkovEntry, the compiler tells me
actual argument java.util.ArrayList<com.company.MarkovEntry<T>> cannot be converted
to java.util.ArrayList<java.lang.Comparable> by method invocation.
What is going on here? MarkovEntry specifically implements Comparable -- why doesn't the compiler recognize that?
My class Chainable implements Comparable as well, in case that has anything to do with it.
Generics are a little strange in that
ArrayList<SuperType>
is not actually a supertype of
ArrayList<SubType>
e.g. ArrayList<Number> is not a supertype of ArrayList<Integer>. This is because if such a relationship held you could substitute in an ArrayList<Number> for an ArrayList<Integer>, which would then allow operations that would have been illegal if you didn't make the replacement.
To be more specific, say you did this:
ArrayList<Number> list = new ArrayList<Integer>();
You'd then be able to put in a Double into list because to the compiler, list is an ArrayList<Number>! As you can see, this breaks the guarantees that generics should provide, so it isn't allowed.
What you're looking for is a generic method like this:
public static <T extends Comparable<? super T>> int binSearch(ArrayList<T> list)
Basically, you can generify methods the same way you can generify classes.
More info can be found here: http://docs.oracle.com/javase/tutorial/extra/generics/methods.html