Generics and wildcards with collections in Java - java

In a test class using AssertJ, I have code similar to the following:
public void someTest() {
assertThat(getNames()).has(sameNamesAs(getExpectedNames()));
assertThat(getNames()).doesNotHave(sameNamesAs(getOtherNames()));
}
private List<String> getNames() {
return null;
}
private List<String> getExpectedNames() {
return null;
}
private List<String> getOtherNames() {
return null;
}
private Condition<List<String>> sameNamesAs(List<String> rhs) {
return new Condition<List<String>>("same names as " + rhs) {
#Override
public boolean matches(final List<String> lhs) {
return lhs.containsAll(rhs) && rhs.containsAll(lhs);
}
};
}
I get a compilation error on the calls to has and doesNotHave:
has/doesNotHave
(org.assertj.core.api.Condition<? super java.util.List<? extends java.lang.String>>)
in AbstractListAssert cannot be applied
to
(org.assertj.core.api.Condition<java.util.List<java.lang.String>>).
I'm new to Java and I don't understand the problem: java.util.List is a super-type of java.util.List and java.lang.String extends java.lang.String, don't they?

In your case, the has and doesNotHave methods take a Condition<? super List<? extends T> condition, not a Condition<? super List<T>> as you are returning from your Condition<List<T>> sameNamesAs method.
You need an instance of the Condition<List<? extends String>> type (it's a subclass of the original type Condition<? super List<? extends String>>):
private Condition<List<? extends String>> sameNamesAs(List<String> rhs) {
return new Condition<List<? extends String>>("same names as " + rhs) { ... };
}
I tried to illustrate this with the following snippet:
List<String> list = getNames();
// ELEMENT = String, ACTUAL = List<? extends ELEMENT>
ListAssert<String> assertThat = assertThat(list);
// by the signature, we have to pass Condition<? super ELEMENT> or Condition<? super ACTUAL>
// Condition<? super ACTUAL> = Condition<? super List<? extends String>>
Condition<List<? extends String>> condition = sameNamesAs(list);
// Condition<List<? extends String>> extends Condition<? super List<? extends String>>
assertThat.has(condition);

Related

Java generic List name clash, has same erasure

I am writing a method with a generic List<T> as an argument. I want to limit T to Integer, Float and Double with this:
private Method(List<T> list) {
this.list = list;
}
public static <T extends Integer> Method<T> create(List<T> list) {
return new Method<>(list);
}
public static <T extends Float> Method<T> create(List<T> list) {
return new Method<>(list);
}
public static <T extends Double> Method<T> create(List<T> list) {
return new Method<>(list);
}
But I get this error:
error: name clash: <T#1>create(List<T#1>) and <T#2>create(List<T#2>) have the same erasure
public static <T extends Float> Method<T> create(List<T> list) {
^
where T#1,T#2 are type-variables:
T#1 extends Float declared in method <T#1>create(List<T#1>)
T#2 extends Integer declared in method <T#2>create(List<T#2>)
I get the same error for T#1 extends Double as well.
The code is based on this answer, which works well. So I think the problem is related to the fact that I used a list of generics as an input instead of a single generic.
How can I fix this? Is there some way to give Java the ability to discern between the different instances?
You could use the Number superclass of Integer, Float, and Double as your bound.
public class Method<T extends Number> {
private final List<T> list;
public Method(List<T> list) {
this.list = list;
}
public static void main(String[] args) {
var mFloats = new Method(Arrays.asList(1.0f, 2.0f, 3.0f));
var mDoubles = new Method(Arrays.asList(1.0,2.0,3.0));
var mInts = new Method(Arrays.asList(1,2,3));
}
}
Type Erasure affects Generic Collections like List, so each generic method is type erased into having a parameter signature with parameter type Obejct. See Oracle Docs To avoid this you can use arrays instead.
import java.util.Arrays;
import java.util.List;
public class Method<T> {
private final List<T> list;
private Method(List<T> list) {
this.list = list;
}
public static <T extends Integer> Method<T> create(T[] arr) {
return new Method<>(Arrays.asList(arr));
}
public static <T extends Float> Method<T> create(T[] arr) {
return new Method<>(Arrays.asList(arr));
}
public static <T extends Double> Method<T> create(T[] arr) {
return new Method<>(Arrays.asList(arr));
}
public static void main(String[] args) {
var floatMethod = Method.create(new Float[] {1.0f, 2.0f});
var doubleMethod = Method.create(new Double[] {1.0, 2.0});
var intMethod = Method.create(new Integer[] {1, 2});
}
}

Is there a way to use a generic argument's own generic argument in Java?

I have this interface, with two generic parameters, and a Descriptor<T> class:
public interface DescriptorFinder<D extends Descriptor<T>, T> {
D find(Class<? extends T> describedClass);
}
public abstract class Descriptor<T> {
private final Class<? extends T> describedClass;
public Descriptor(Class<? extends T> describedClass) {
this.describedClass = describedClass;
}
public Class<? extends T> getDescribedClass() {
return describedClass;
}
}
Using those foundations I can create an NumberDescriptor to describe a number:
public class NumberDescriptor extends Descriptor<Number> {
private boolean handlesDecimalPart;
public NumberDescriptor(Class<? extends Number> describedClass) {
super(describedClass);
}
public boolean handlesDecimalPart() {
return handlesDecimalPart;
}
public void setHandlesDecimalPart(boolean handlesDecimalPart) {
this.handlesDecimalPart = handlesDecimalPart;
}
}
And then have a DescriptorFinder for it:
public class NumberDescriptorFinder implements DescriptorFinder<NumberDescriptor, Number> {
#Override
public NumberDescriptor find(Class<? extends Number> describedClass) {
NumberDescriptor descriptor = new NumberDescriptor(describedClass);
if (describedClass == Double.class || describedClass == Float.class) {
descriptor.setHandlesDecimalPart(true);
}
return descriptor;
}
}
Well, that DescriptorFinder<NumberDescriptor, Number> is pretty weird to use, I would like to remove the second generic parameter and reduce it to DescriptorFinder<NumberDescriptor>. So I tried something like this:
public interface DescriptorFinder<D extends Descriptor<?>> {
D find(Class<? extends T> describedClass); // How to get T?
}
But I couldn't find a way to get D's T argument, for example, if I had D as Descriptor<Number>, I want to get Number so I can have Class<? extends Number>.

Getter and setter for bounded wild card type property

I have a class that contains a List<? extends BaseType>. Now BaseType can have two subtypes: SubTypeA and SubTypeB. At runtime the list may be either a List<SubTypeA> or List<SubTypeB>. How should the signature of getter and setter look for this property?
class ListHolder{
private List<? extends BaseType> listOfBaseType;
//getter and setter for listOfBaseType
}
Do we have to declare type parameter in the getter?
It depends, if you want to stick with the superclass just make it that way :
Generic typing 1
class ListHolder{
private List<? extends BaseType> listOfBaseType;
//getter and setter for listOfBaseType
public List<? extends BaseType> getListOfBaseType(){...}
public void setListOfBaseType(List<? extends BaseType>){...}
}
Exemple 1
ListHolder lh = new ListHolder();
lh.setListOfBaseType(new ArrayList<BaseTypeA>()); //OK
lh.setListOfBaseType(new ArrayList<BaseTypeB>()); //OK
List<BaseTypeB> l = lh.getListOfBaseType(); //KO too specific
List<? extends BaseType> l = lh.getListOfBaseType(); //OK
Generic typing 2
If you want strong control and safe conversion (although with this one you can't mix up types) :
class ListHolder<T extends BaseType>{
private List<T> listOfBaseType;
//getter and setter for listOfBaseType
public List<T> getListOfBaseType(){...}
public void setListOfBaseType(List<T>){...}
}
Exemple 2
ListHolder<BaseTypeA> lh = new ListHolder<BaseTypeA>();
lh.setListOfBaseType(new ArrayList<BaseTypeA>()); //OK
lh.setListOfBaseType(new ArrayList<BaseTypeB>()); //KO will not compile
List<BaseTypeB> l = lh.getListOfBaseType(); //KO will not compile
List<BaseTypeA> l = lh.getListOfBaseType(); //OK no need to cast
Generic typing 3
If you want more flexibility while keeping safety :
class ListHolder{
private HashMap<Class<? extends BaseType>, List<? extends BaseType> hashListOfBaseType;
//getter and setter for listOfBaseType
public <T extends BaseType> List<T> getListOfBaseType(Class<T> clazz){
return hashListOfBaseType.get(clazz);
}
public <T extends BaseType> void setListOfBaseType(Class<T> clazz, List<T> list){
hashListOfBaseType.put(clazz, list);
}
}
Exemple 3
ListHolder lh = new ListHolder();
lh.setListOfBaseType(BaseTypeA.class, new ArrayList<BaseTypeA>()); //OK
lh.setListOfBaseType(BaseTypeB.class, new ArrayList<BaseTypeB>()); //OK
List<BaseTypeA> l = lh.getListOfBaseType(BaseTypeA.class); //OK
List<BaseTypeB> l = lh.getListOfBaseType(BaseTypeB.class); //OK
Everything depends on what you need.... elaborate then I can be more specific
public List<? extends BaseType> getListOfBaseType() {
return listOfBaseType;
}
public void setListOfBaseType(List<? extends BaseType> listOfBaseType) {
this.listOfBaseType = listOfBaseType;
}

How to convert one generic list to another in java

I am trying to convert one generic list of enums to another generic list type but always getting the following compile error:
Is not applicable for the arguments
private static <T extends Enum<T>> List<T> enumListFromEnumList(List<Object> sourceEnumsList, Class<T> classObject) {
List<T> enums = new ArrayList<T>();
if(sourceEnumsList != null) {
for(Object enumObject : sourceEnumsList) {
if (enumObject instanceof Enum)
enums.add(Enum.valueOf(classObject, enumObject.toString().toUpperCase()));
}
}
return enums;
}
Even I tried this:
private static <T extends Enum<T>> List<T> enumListFromEnumList(List<T> sourceEnumsList, Class<T> classObject) {
List<T> enums = new ArrayList<T>();
if(sourceEnumsList != null) {
for(T enumObject : sourceEnumsList) {
enums.add(Enum.valueOf(classObject, enumObject.toString().toUpperCase()));
}
}
return enums;
}
This is how I'm consuming the function:
adapterInfo.setResponseTypeList( enumListFromEnumList(info.getResponseTypeList(), CAPInfo.ResponseType.class));
Updates:
It works when I convert to this:
private static <S extends Enum<S>, T> List<S> enumListFromEnumList(List<T> sourceEnumsList, Class<S> classObject) {
List<S> enums = new ArrayList<S>();
if(sourceEnumsList != null) {
for(T enumObject : sourceEnumsList) {
enums.add(Enum.valueOf(classObject, enumObject.toString().toUpperCase()));
}
}
return enums;
}
So now the question is how to translate "<S extends Enum<S>, T> List<S>" part ( I mean how it works)
Here is what you are looking for (I think) :
enum EnumA {
FIRST,
SECOND,
THIRD
}
enum EnumB {
FIRST,
SECOND,
THIRD
}
private static <A extends Enum<A>, B extends Enum<B>> List<B> enumListFromEnumList(List<A> sourceEnumsList, Class<B> classObject) {
List<B> enums = new ArrayList<B>();
if (sourceEnumsList != null) {
for (A enumObject : sourceEnumsList) {
enums.add(Enum.valueOf(classObject, enumObject.toString().toUpperCase()));
}
}
return enums;
}
public static void main(String[] args) {
List<EnumA> listA = new ArrayList<EnumA>();
listA.add(EnumA.FIRST);
listA.add(EnumA.THIRD);
List<EnumB> listB = enumListFromEnumList(listA, EnumB.class);
System.out.println(listB);
}

java - How to get the type of the parameter from a parameterized argument

I'm having troubles trying to find a solution, if any, to this:
public class Generics {
Map<Class<? extends SomeObject1>, SomeObject2>> map;
map = new HashMap<Class<? extends SomeObject1>, SomeObject2>>();
public static <E extends SomeObject1> SomeObject2 get(Class<E> c) {
if (map.containsKey(c))
return map.get(c);
else {
SomeObject2 o = new SomeObject2();
map.put(c, o);
return o;
}
}
}
...
//somewhere
public <T extends SomeObject1> void aMethod(AnInterestedClass<T> list) {
// How to get the value from the map
// knowing that the key is of type T?
Generics.get();
}
Ideas?
Because of type erasure, you can only do this by passing a Class object to aMethod. See this related thread.

Categories