I would like to create a method which returns type is a map object and parameter should be a class which extends A and implements I.
So my code is as follows:
public Map<String,String> getIdea(Class < ? extends A & I) { .....}
But i am getting a compilation error saying that my syntax is wrong. It is expecting a comma right after A. It does not work even with comma. Do you have any idea?
To put what Sotirios Delimanolis said in comments into code:
public <T extends A & I> Map<String, String> getIdea(Class<? extends T> clazz) { }
To be honest, I don't think that the wildcard gets you anything here since T is tightly bound, so you may be better off with the non-wildcard version.
For parameter to be a class which extends A and implements I, type parameter should be defined at method level as below:
public <T extends A & I> Map<String,String> getIdea(T t) { .....}
Related
I have the following scenario in Java generics:
public abstract class A<T> {
protected final Class<T> typeOfX;
public A(final Class<T> typeOfX) {
this.typeOfX = typeOfX;
}
public abstract void load(final T x);
}
public class AnyA<S> extends A<S> {
private final Map<String, A<? extends S>> map;
public AnyA(final Class<S> superTypeOfX,
final Map<String, A<? extends S>> map) {
super(superTypeOfX);
this.map = map;
}
#Override
public void load(final S superx) {
for (final A<? extends S> a: map.values())
if (a.typeOfX.isAssignableFrom(superx.getClass())) //Here I want to say: "if superx can be casted to a.typeOfX".
a.load(a.typeOfX.cast(superx)); //Here I want to cast superx to a.typeOfX (so as to call the load method). Here's the compile error.
}
}
I'm getting the error:
incompatible types: S cannot be converted to CAP#1
where S is a type-variable:
S extends Object declared in class AnyA
where CAP#1 is a fresh type-variable:
CAP#1 extends S from capture of ? extends S
AnyA is a composite A, i.e. is an A which maintains several other A instances.
AnyA in its load(...) method shall decide which of the maintained A instances should be used to "pass the loading process to" of the argument.
In other words, AnyA is responsible for finding the correct A to load the argument.
But also AnyA is an A because it handles loading the argument.
My question is:
Why is this cast not possible, by the time I know that S is a sub-class of T and all A instances in AnyA can load a subclass of S?
How can I overcome this problem without changing the class diagram too much?
I have read about "helper methods" but cannot match the example shown there to my problem.
I'm using NetBeans IDE with Java SDK 8.
Note that regardless of what you do, the code is not "syntactically type safe" in any case. There is an unchecked cast, and the only safety belt that prevents this from going wrong is the isAssignableFrom check.
(That is often OK, I'm just mentioning it for completeness)
The reason for the error may be more obvious when you pull the lines apart (here, S stands for SuperType, according to the Type Parameter Naming Conventions - please follow them!)
A<? extends S> a = ...;
S s = a.typeOfX.cast(s);
a.load(s);
The A<? extends S> intuitively means that it is an A that can accept an unknown type in its load method. You know that it extends type S, but you do not know which type this is.
It may become blatantly obvious when you insert Object for S:
A<String> specificA = ...;
// So the "specificA" can load "String" objects. Then this is fine:
A<? extends Object> a = specificA;
Object s = a.typeOfX.cast(s);
// But here's the error: "s" is only an Object, and not a String!
a.load(s);
I think the main point of confusion (and the main reason for the question) was the following: When calling
Object s = a.typeOfX.cast(s);
and typeOfX is String.class, then the return type of the cast will not be String, but only the type that the compiler can infer at this point. And this is Object, in the example above.
However, you already referred to the Helper Methods, and indeed, with some trickery, you can make this compile,
but... (see notes below)
import java.util.Map;
abstract class A<T>
{
protected final Class<T> typeOfX;
public A(Class<T> typeOfX)
{
this.typeOfX = typeOfX;
}
public abstract void load(T x);
}
class AnyA<S> extends A<S>
{
private final Map<String, A<? extends S>> map;
public AnyA(Class<S> superTypeOfX,
Map<String, A<? extends S>> map)
{
super(superTypeOfX);
this.map = map;
}
#Override
public void load(S s)
{
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(s.getClass()))
{
callLoad(a, s);
}
}
}
private static <S, T extends S> T cast(A<T> a, S s)
{
T t = a.typeOfX.cast(s);
return t;
}
private static <T, S extends T> void callLoad(A<S> a, T s)
{
a.load(cast(a, s));
}
}
I would not recommend this in practice.
Personally and subjectively: I think that when you are doing the isAssignableFrom check, then the (unchecked) cast should be as close as possible to this check. Otherwise, the code will be very hard to understand.
So although unchecked casts are a code smell in practice, and I try to avoid SuppressWarning whenever possible, I would consider this as far more readable:
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(superx.getClass()))
{
// This call is safe as of the check done above:
#SuppressWarnings("unchecked")
A<Object> castA = (A<Object>) a;
castA.load(superx);
}
}
I have all kinds of GameObjects. I want to make basic collection definition for them all:
//Wrong number of type arguments; required 2. > expected
public interface GameObjectMap<T> extends Map<String, T extends GameObject> {
}
The collection will allways be mapped by string (because the data are loaded from JSON). But the second generic type argument should be any instance of GameObject. I have no idea how to write the code above correctly.
You're almost right. Simply move the extends GameObject to the first generic definition:
public interface GameObjectMap<T extends GameObject> extends Map<String, T> {
}
The restriction comes on the first declaration of T, like
public interface GameObjectMap<T extends GameObject> extends Map<String, T> {
}
What's wrong with this method definition?
public static List<T extends MyObject> T find() {
}
Compiler says:
Syntax error, insert ";" to complete MethodDeclaration
You have two return types there.
If you wanted to introduce a generic type T that would be
public static <T extends MyObject> List<T> find() {}
The proper method declaration would be:
public static <T extends MyObject> List<T> find() { ... }
When creating (static) generic methods, the generic parameter(s) has to be defined before the return-type, because they may be used in the return-type.
I'm trying to get interface instance depending on what type T is. Place or something else that extends BaseDictionary.
public static <T extends BaseDictionary> IDictionaryDataSource<T> getEntityDataSourceInstance(Class<T> clazz,
Context cxt) {
if (Place.class.isAssignableFrom(clazz)) return (IDictionaryDataSource<T>) new PlaceDataSource(cxt);
//here some other types, same lines
return null;
}
public abstract class BaseDictionary{}
public class Place extends BaseDictionary{}
public interface IDictionaryDataSource<T extends BaseDictionary>{}
public abstract class BaseDictionaryDataSource<T extends BaseDictionary> implements IDictionaryDataSource<T>{}
public class PlaceDataSource extends BaseDictionaryDataSource<Place>{}
And I get
Type mismatch: cannot convert from PlaceDataSource to IDictionaryDataSource<T>
or
Type safety: Unchecked cast from PlaceDataSource to IDictionaryDataSource<T>
if I cast it like above.
Can you explain why do compile error and warning occur?
It will be called here
public static <T extends BaseDictionary> DictionaryElementPickerFragment<T> newInstance(Class<T> clazz, Context cxt){
//somecode here
fragment.setDataSource(DictUtils.getEntityDataSourceInstance(clazz, cxt));
}
I've tried to find answer here and in google but no success.I would appreciate any help.
Now I think like this
There is no helper method to work around the problem, because the code is fundamentally wrong.
Thanks in advance.
This more concrete example illustrates your problem which is one of type parameter variance.
void foo(List<String> stringList, Integer anInteger) {
List<Object> objList = (List<Object>) stringList;
objList.add(anInteger); // Violation -- adding an object to a list of strings
// could cause someone getting a "String" to get an
// Integer stead
}
so a List<String> is not a List<Object> although it is a List<? extends Object>.
In your specific instance you can't cast
PlaceDataSource to IDictionaryDataSource<T>
PlaceDataSource is an IDictionaryDataSource<Place>, but the only thing we know about <T> is that it extends BaseDictionary which is a super-class of BaseDictionary.
So you can cast a PlaceDataSource to
an IDictionaryDataSource<Place> or to
an IDictionaryDataSource<? super Place> or to
an IDictionaryDataSource<? extends BaseDictionary>
but not to an IDictionaryDataSource<T> because T is not guaranteed to be Place, and doing so would lead to a mismatch between the actual type parameter Place and the formal type parameter T.
That is not secure implementation and it is dangerous to cast because (Place.class.isAssignableFrom(clazz) saves you here):
T is a generic type and you're replacing it with definite Place.
What if I have
public class Home extends BaseDictionary{}
public class HomeDataSource extends BaseDictionaryDataSource<Home>{}
And then I invoke getEntityDataSourceInstance with Home class but get PlaceDataSource which cannot be cast to HomeDataSource (IDictionaryDataSource) which I expect. So I'll end up having ClassCastException.
It looks like getEntityDataSourceInstance should be an instance method in the BaseDictionary class, not a static method.
A subclass will know which type of DictionaryDataSource to create.
Try #SuppressWarnings("unchecked")
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>