Java generics warnings on java.util.Collections - java

I have a method:
public List<Stuff> sortStuff(List<Stuff> toSort) {
java.util.Collections.sort(toSort);
return toSort;
}
This produces a warning:
Type safety: Unchecked invocation sort(List<Stuff>) of the generic method sort(List<T>) of type Collections.
Eclipse says the only way to fix the warning is to add #SuppressWarnings("unchecked") to my sortStuff method. That seems like a crummy way to do with something that is built into Java itself.
Is this really my only option here? Why or why not? Thanks in advance!

Collections.sort(List<T>) expects that T must implement Comparable<? super T>. It seems like Stuff does implement Comparable but doesn't provide the generic type argument.
Make sure to declare this:
public class Stuff implements Comparable<Stuff>
Instead of this:
public class Stuff implements Comparable

Do tou use this:
// Bad Code
public class Stuff implements Comparable{
#Override
public int compareTo(Object o) {
// TODO
return ...
}
}
or this?
// GoodCode
public class Stuff implements Comparable<Stuff>{
#Override
public int compareTo(Stuff o) {
// TODO
return ...
}
}

You will need to change the return type of your method

Sorting Generic Collections
There are two sorting functions defined in this class, shown below:
public static <T extends Comparable<? super T>> void sort(List<T> list);
public static <T> void sort(List<T> list, Comparator<? super T> c);
Neither one of these is exactly easy on the eyes and both include the wildcard (?) operator in their definitions. The first version accepts a List only if T extends Comparable directly or a generic instantiation of Comparable which takes T or a superclass as a generic parameter. The second version takes a List and a Comparator instantiated with T or a supertype.

Related

Unexpected behaviour with generics

I'm facing this error while work with Java generics, and I don't understand which is the problem;
I have two classes like these:
public abstract class BaseClass{
....
}
public class OneClass extends BaseClass{
....
}
I have a generic repo for OneClass:
public class MyRepo<T extends BaseClass>{
List<T> getElements();
}
Then I have a method that should works whit generics:
private MyRepo<OneClass> myRepo;
public <T extends BaseClass> List<T> myMethod(){
return myRepo.getElements();
}
The method doesn't work unless I force a cast to List ( as shown below ):
public <T extends BaseClass> List<T> myMethod(){
return (List<T>) myRepo.getElements();
}
I don't understand which is the problem given that OneClass extends BaseClass.
Thanks for any suggestion.
Having a method of the form:
<T> T myMethod()
makes the inference of the actual T dependent on the call-site:
String s = myMethod();
Integer i = myMethod();
Considering your scenario one could invoke your method like this:
List<BaseClass> a = myMethod();
List<OneClass> a = myMethod();
As you can see this can be incorrect as myMethod could actually return another subtype of BaseClass (lets say TwoClass) which is not correct to cast to List<OneClass> - thus you need the unsafe cast to List<T>.
You should change the signature of myMethod to one of the following:
public List<? extends BaseClass> myMethod(){}
public List<BaseClass> myMethod(){}
The first variant states that this is a list of any subtype of BaseClass the other just omits that information.
Dependent on what you want to achieve check the other answer or read about PECS (Producer Extends, Consumer Super) or f-bounded polymorphism / self-types to return the concrete type.
Error message tells you that not every T extends BaseClass is OneClass
You should make sure that the field myRepo is the same type as T as in your method. If you force it to be OneClass you cant use othere types except OneClass. So there is no use of a generic. If you want to allow every extending class from BaseClass you could make the class of the mehtod generic in oder to use the same type of T as shown below:
public class FunctionClass<T extends BaseClass> {
private MyRepo<T> myRepo;
public List<T> myMethod(){
return myRepo.getElements();
}
}

How to check whether Java generic type is subclass of Comparator?

When I try to complete a task, there's one case need to be handled: throw exception of generic type of class is not comparable. Refer to the following code for the detail.
public class C <T>
{
public C()
{
// throw exception if T is not comparable
}
}
You can enforce the generic to be a subclass of Comparator like so:
public class C <T extends Comparator> {
public C(){
}
}
As you see in the below code, it would be a good idea to add another generic (here it is K), which you supply to Comparator, since the generic of Comparator will otherwise default to Object.
public class C <K, T extends Comparator<K>> {
public C(){
}
}
You generally use this in the form of T x K, where T is the generic, x is super or extends and K is the class/interface.
Comparator docs
You should make sure that the generic parameter T is a Comparableby writing:
public class C <T extends Comparable>
There are two ways:
1) Make it T extends Comparable, so you know it will always be.
2) In the constructor, pass Class < T > as a parameter, so you'll know at runtime what T is. (because it's erased)
Add a generic constraint, that is would be better since it will be handle it at compile time rather than throw an exception on runtime.
class C <T extends Comparable>
You should verify that your parameter T is Comparable.
public class C <T extends Comparable>
This is the same if you want a generic type implements some interface.
public class C <T implements <interface what you want> >
Or if you want that were a superclass of another one.
public class C <T super <class what you want> >
If you always want the type T to implement Comparable you can enforce this as follows.
public class C<T extends Comparable<? super T>> {
public C() {
}
}
This is better than throwing an exception at runtime. It will not even compile if someone tries to write new C<some non-Comparable type>(). Note that Comparable is a generic type, so it should not be used without type parameters; it should not simply be
public class C<T extends Comparable>
If the type T will not always implement Comparable, but you want a specific constructor that will only work for Comparable types, then the answer is that you can't do this with constructors, but you can do it with a static method.
public class C<T> {
public C() {
// Constructor that works with any T
}
public static <T extends Comparable<? super T>> C<T> getNew() {
C<T> c = new C<>();
// Do stuff that depends on T being Comparable
return c;
}
}

The correct way of extending an ArrayList and and implementing an interface using Generics

I need to implement an interface that has two generic type arguments and a method that takes one generic type as the argument and the other generic type as a return type. This is how I implemented it but I don't know if it's the right way.
public interface Evaluate<K, T>
{
T use(K k);
}
Also, I need to extend the Java's ArrayList and add methods to it such as map. The map() method needs to take one argument of type Evaluate and return a new list. I don't know if I should implement the interface and how to actually pass the argument in my method. This is my attempt so far:
public class Array<K, T> extends java.util.ArrayList<T> implements Evaluate<K,T>
{
public ArrayList<T> map(Evaluate f1)
{
}
}
Do it either:
public class Array<K, T> extends java.util.ArrayList<T> implements Evaluate<K,T> {
T use(K k) {
// implementation
}
public java.util.ArrayList<T> map() {
// use(K) somewhere here
}
}
or
public class Array<K, T> extends java.util.ArrayList<T> {
public java.util.ArrayList<T> map(Evaluate<K,T> evaluate) {
// use evaluate somewhere here
}
}
Your implementation does't have definition of T use(K) declared in interface - you can leave it unimplemented only in an abstract class or an interface extending another one. Also your argument is raw since you didn't write it like map(Evaluate<K,T> f1) (Java doesn't automatically substitute generic parameters).

Java Generics insertion on wildcards

Java is not allowing me to add a subclass of the Type declaration in this class
public class Exam<T> {
public void set(Holder<? super T> hold){
}
public T get(Holder<? extends T> holder){ return holder.get();}
public static void main (String[] args){
Exam<Question> eq = new Exam<Question>();
eq.set(new Holder<Identification>());
}
}
Where Identification is a subclass of Question.
and this how my holder class looks like
public class Holder<T> {
T item;
public void set(T item){ this.item = item; }
public T get(){return item;}
}
ERROR
The method set(Holder<? super Question>) in the type Exam<Question> is not applicable for the arguments (Holder<Identification>)
The error looks pretty self-explanatory to me - the set method expects a Holder<? super Question> and you're trying to give it a Holder of something that is a subclass of Question. As written, Exam.set could take a Holder<Object>, for example, but not a Holder<Identification>.
A good way to think about extends and super in generics is in terms of assignment: T extends Foo will accept any type T that you could use on the right hand side of an assignment to Foo without casting, i.e.
Foo something = new T();
(treat this as pseudocode - I know you're not really allowed to new a type varaible). Conversely, T super Foo accepts any T you could use on the left hand side of an assignment without casting:
T myThing = new Foo();
In your specific example, Identification i = new Question() isn't legal without a cast, so a Holder<? super Question> parameter can't accept a Holder<Identification> value.
Exam<T> expects a Holder<T> that can hold any subclass of T. That's what the super does. You are passing a Holder<Identification> but Identification is neither T nor a superclass of it.
change set method of class Enum to
public void set(Holder<? extends T> hold){
}
Generic is invariant. That could be an answer for you. Invariant means that Type2 is a subclass of Type1 so it doesn't mean that List is a List.
It works (but has a different meaning, of course) if you write
public void set(Holder<? extends T> hold){ }

Javac error on Generic return type

It might sound like reinvention of wheel, but I am trying to implement a map, (like Map<K,V>). The class has a function called sortedKey() which returns an ArrayList<K> A cut-down version of my code is below. I have included my attempts to debug inline as comments.
import java.util.ArrayList;
import java.util.Collections;
public class Map<K,V> {
private ArrayList<Pair<K,V> > array; //Pair<K,V> is a class defined in another file.
//returns an ArrayList(of proper type) of keys (ie, the desired function)
public ArrayList<K> sortedKeys(){
ArrayList<K> ret = keys(); //another method defined inside same class
K s = ""; // error: no suitable method found for sort(ArrayList<K>)
Collections.sort(new ArrayList<String>()); //Works just fine..
Collections.sort(ret); //Same error here..
return ret;
}
}
Any idea on why is that error showing up? Can I not have generic return types depending on the type-variable used in creation of the class? Or do I have to do something else to achieve the desired effect?
Thanks and apologies if this question has already been asked
Cajetan
Have a look at the signature of Collections.sort:
public static <T extends Comparable<? super T>> void sort(List<T> list)
So the error, though it might be confusing, is right - you can't call sort on a list of arbitrary type; the element type must implement Comparable.
If you restrict your generic parameter to be comparable, as in:
public class Map<K extends Comparable<K>,V> {
...
then the call to Collections.sort(ret) will succeed as you expect.
Without this restriction on the generic parameter, someone could create a Map with a key type of something noncomparable like Exception - and then how do you expect poor Collections.sort to handle that? :)
The compiler is telling you that K might not be a type with an ordering. You should declare the class as
public class Map<K extends Comparable<K>, V> {
to guarantee that K values can be compared to other K values.
From the Javadoc for Collection.sort() the type has to be
public static <T extends Comparable<? super T>> void sort(List<T> list)
In other words, K has to be declared as Comparable<K> or Comparable<? super K>
Well...you should provide a comparator or implements comparable by your K(whichever it may be) class.
in case of implementing comparable, Also declare class parameter like this to restrict it to contain only comparable object.
public class Map<K extends Comparable<K>, V>

Categories

Resources