How to pass generics using wildcard to a function without casting? - java

Here is my problem:
my function in class A:
public void setData(Map<String,? extends ArrayList<? extends SomeInterface>>){...}
my call:
Map<String, ArrayList<ImplementsSomeInterface>> a=...;
instanceOfA.setData(a); //does not compile
instanceOfA.setData((Map<String,? extends ArrayList<? extends SomeInterface>>) a); // works thanks to the Casting.
I don't think this is clean. Is there a way to avoid the casting without droping the wildcard use in the function?

First your setData method should read:
public void setData(Map<String,? extends List<? extends SomeInterface>>)
Your map declaration should read:
Map<String, List<ImplementsSomeInterface>> a = ...;
Which is pretty much what you've got following your edits to the original question. The change I've made from ArrayList to List doesn't effect the behaviour of the code.
Following comments:
public static void main()
{
Map<String, List<Double>> map = new HashMap<String, List<Double>>();
map.put("prices", new ArrayList<Double>(Arrays.asList(1.1, 2.2, 3.3)));
setData(map);
}
public static void setData(Map<String,? extends List<? extends Serializable>> map)
{
}

The problem is that setData takes as an argument a
Map<String,? extends ArrayList<? extends SomeClass>>Map>
whereas in your first call(the one that doesn't compile) you are trying to pass it a Map<String,? extends SomeClass>
Java cannot automatically cast a subclass of
SomeClass
to a subclass of
ArrayList<? extends SomeClass>
because it is not neccesarily an instance of ArrayList.

Related

Java Generics questions for multiple type parameters

I have this code:
public interface IDimension<S extends IDimension<S,T>, T extends Number> extends Comparable<S> {
T toNumber();
default T toBaseNumber() {
return toNumber();
}
S fromNumber( T units );
Class<T> numberType();
}
When I implement IDimension like below(Implementation1), I get 'Type parameter 'TestBaseSample' is not within its bound; should implement 'IDimension<TestBaseSample<Float,Integer>, java.lang.Long>' error:
Implementation1 :
class TestBaseSample<Integer, Float>
implements IDimension<TestBaseSample<Float, Integer>, Long> {
}
I understand why 'Implementation1' gives error, but not able to understand why 'Implementation2' and 'Implementation3' works?
Implementation2:
class TestBaseSample2<Integer, Float>
implements IDimension<TestBaseSample2<Float, Float>, Long> {
}
Implementation3 :
class TestBaseSample3<Integer, Float>
implements IDimension<TestBaseSample3<Integer, Integer>, Long> {
}
It looks like you are attempting to create a concrete type, but you are actually creating generic parameters and giving them the names of java classes.
Consider your first example TestBaseSample2 if we change the generic parameters to T and S
class TestBaseSample2<T, S>
implements IDimension<TestBaseSample2<S, S>, Long> {
}
Now, you should be able to make an instance provided S is a Long and T can be anything else.
Here is a simpler example.
static class OtherList<Integer> extends ArrayList<Integer>{}
public static void main (String[] args) throws java.lang.Exception
{
OtherList<String> list = new OtherList<>();
list.add("this");
System.out.println(list.size());
}
Remove all types from your implementations (and never name generic parameters using class names, especially classes from the JDK - use single letters):
class TestBaseSample implements IDimension<TestBaseSample, Long> {
}
class TestBaseSample2 implements IDimension<TestBaseSample2, Long> {
}
class TestBaseSample3 implements IDimension<TestBaseSample3, Long> {
}

Issue with interface as Map key & value

In a library project, I have :
public interface InterfaceA {
}
public interface InterfaceB {
}
public void myMethod(Map<? extends InterfaceA, List<? extends InterfaceB>> map) {
//do something
}
Then I have another project (having this library as a dependency) that contains two object implementing these interfaces :
public class ObjectA implements InterfaceA {
}
public class ObjectB implements InterfaceB {
}
When I try to call the library method myMethod like this :
HashMap<ObjectA, List<ObjectB>> hashMap = new HashMap<>();
//populate hashmap
myMethod(hashMap);
I get a compilation warning saying there is an argument mismatch.
What am I missing here ? Does it have something to do with the map ?
EDIT :
The exact error (it's not a warning actually) is :
incompatible types: HashMap<ObjectA,List<ObjectB>> cannot be converted to Map<? extends InterfaceA,List<? extends InterfaceB>>
Generics are invariant.
If your method declares:
Map<? extends InterfaceA, List<? extends InterfaceB>>
Then the second type parameter has to be exactly List<? extends InterfaceB>.
You can fix it by using:
Map<? extends InterfaceA, ? extends List<? extends InterfaceB>>
Instead.
You either modify your Hashmap creation for this:
Map<? extends InterfaceA, List<? extends InterfaceB>> hashMap = new HashMap<>();
or modify your method definition for this:
public <A extends InterfaceA, B extends InterfaceB> void myMethod(Map<A, List<B>> map) {
//do something
}
Declare your map as
HashMap<ObjectA, List<? extends InterfaceB>> hashMap = new HashMap<ObjectA, List<? extends InterfaceB>>();

Java Generics and Map declaration

I am having a hard time declaring a Map using Generics. I'd like to declare a Map with the following properties:
The key is a Class object of any Type T derived from a particular Interface (IFoo)
The value of the Map is another Map whose key is an String and whose value is of the Type T
I thought I can do it like this:
public static Map<Class<T extends IFoo>, Map<String, T>> valueCache =
new HashMap<Class<T extends IFoo>, Map<String, T>>();
I get a syntax error on "extends"
If I replace the T Types with wildcards (?) like this it seems to be syntactically correct:
public static Map<Class<? extends Typ>, Map<Integer, ?>> valueCache=
new HashMap<Class<? extends Typ>, Map<Integer, ?>>();
But I don't think this is what I want since I want to be exactly the type of the Class object to be the value in the second map.
Any help appreciated.
A generic type parameter can only be declared on a class or method declaration.
If you don't care about the reference type of the IFoo that you get back you can do
static Map<Class<? extends IFoo>, Map<String, IFoo>> fooMap;
If you want to use the IFoo returned as its subclass type then you need to do some casting.
// abbreviated example
class FooMap {
private static Map<Class<? extends IFoo>, Map<String, IFoo>> map = ...;
static void put(String key, IFoo foo) {
map.get(foo.getClass()).put(key, foo);
}
static <F extends IFoo> F get(Class<F> cls, String key) {
return cls.cast(map.get(cls).get(key));
}
}
FooMap.put("foo", new Foo());
Foo foo = FooMap.get(Foo.class, "foo");
Move the extends into your class's generic definition:
public class ClassWithGeneric<T extends IFoo> {
Map<Class<T>, Map<String, T>> valueCache = new HashMap<Class<T>, Map<String, T>>();
}
It's not technically possible to do what you want, but you can simulate it using accessor methods with internal casting. For example:
private static Map<Class<?>, Map<String, ?>> valueCache = new HashMap<>();
public <T extends IFoo> Map<String, T> getMap(Class<T> key) {
return (Map<String, T>)value cache.get(key);
}
Try this,
public static Map<IFoo, Map<String, IFoo>> valueCache = new HashMap<IFoo, Map<String, IFoo>>();
By this way, you make use of the map for IFoo Type Classes.
since you Declaring a Map you should Specify all types it helps a lot and should be always Correct so it should be like T should be a type and not Generic since you declare it so java wants to know the type in the newer Versions of java you dont need to declare the second <> generics so i dont know the second type of your inner map so i used Object
public static Map<IFoo,Map<String,Object>valueChache=new Hashmap<>();
Both should be Correct
public static Map<IFoo,Map<String,Object>valueChache=new Hashmap<IFoo,Map<String,Object>();

Using Map in Method with Generics

I am trying to determine if it is possible to use Generics in the following situation. It can best be described by my code. (This code is just an example; I took out most of the code not relevant to the problem I'm having.)
public class FooBar {
public <T extends MyModel> Map<Class<T>, List<T>> convertToModelList(
Map<String, Class<T>> infoMap) {
// do stuff...
}
}
public class MyClient {
public void doSomething() {
Map<String, Class<? extends MyModel>> oldMap = new HashMap<String, Class<? extends MyModel>>();
oldMap.put ("car", Car.class);
oldMap.put("truck", Truck.class);
FooBar f = new FooBar();
Map<Class<? extends MyModel>, List<? extends MyModel>> newMap = f
.convertToModelList(oldMap);
}
}
public class Car extends MyModel {
}
public class Truck extends MyModel {
}
public class MyModel {
}
The compiler is saying that I can't call convertToModelList (in MyClient) because a Map<String, Class<? extends MyModel>> is not equivalent to Map<String, Class<T>>. I somewhat understand why this is occurring, but is there a way around this?
EDIT:
To be more specific, the problem I'd like to solve is using Generics in the convertToModelList() method above. If I can't use generics here, then whatever I return from that method has to be cast on the client. For example, if I change FooBar to this:
public class FooBar {
public Map<Class<? extends MyModel>, List<? extends MyModel>> convertToModelList(
Map<String, Class<? extends MyModel>> infoMap) {
// do stuff...
}
}
If I pass in a
Map<String, Class<Truck>>
to convertToModelList, it will return a
Map<Class<Truck>, List<Truck>>
, however the client won't know it's a Truck - all it will know is that it's of type MyModel - using generics lets me avoid the cast to Truck in the MyClient code:
public class MyClient {
public void doSomething() {
Map<String, Class<? extends MyModel>> oldMap = new HashMap<String, Class<? extends MyModel>>();
oldMap.put ("car", Car.class);
oldMap.put("truck", Truck.class);
FooBar f = new FooBar();
Map<Class<? extends MyModel>, List<? extends MyModel>> newMap = f
.convertToModelList(oldMap);
// I'm trying to avoid this cast
List<Truck> trucks = (List<Truck>)newMap.get(Truck.class);
}
}
Although the common superclass of Car and Truck is MyModel, the common superclass of List<Car> and List<Truck> is Object. The map should be of type Map<Class<? extends MyModel>, Object>>. You better wrap the map with some invariant checks and casting instead of getting info directly from the map.
This may be helpful too: Heterogeneous container to store genericly typed objects in Java
Since it extends MyModel, you are saying you only want that type.
It is valid to make it just MyModel and not ? extends MyModel.
Class<MyModel>
Then, any objects that extends this (which will be all the Car and Truck, will have to call the super method of MyModel. This will mean they are all of this type.

Java nested wildcard generic won't compile

I have a problem with bounded nested wildcards in Java generics.
Here's a common case:
public void doSomething(Set<? extends Number> set) {}
public void callDoSomething() {
Set<Integer> set = new HashSet<Integer>();
doSomething(set);
}
This is standard Java generics, works fine.
However if the wildcard becomes nested, it no longer works:
public void doSomething(Map<String, Set<? extends Number>> map) {}
public void callDoSomething() {
Map<String, Set<Integer>> map = new HashMap<String, Set<Integer>>();
doSomething(map);
}
This leads to a compiler error.
I've tried a variety of casts and wildcard permutations, but I'm unable to get this working. I don't recall seeing this issue before, and I've worked with generics for years. Am I just too tired and missing something obvious?
So the problem is, doSomething could be implemented as:
public void doSomething(Map<String, Set<? extends Number>> map) {
Set<Float> set = ...;
map.put("xyz", set);
}
You need to decide what you actually mean.
Probably something like:
public void doSomething(Map<String, ? extends Set<? extends Number>> map) {}
this will work for you:
public void doSomething(Map<String, ? extends Set<? extends Number>> map) {}
To make code to work Create HashMap as:
Map<String, Set<? extents Number>> map = new HashMap<String, Set<? extents Number>>();

Categories