I am pretty sure this is type safe, but just wanted to check as Eclipse is asking me to put a #SuppressWarnings("unchecked") annotation.
Map<String, IFace> faces;
public <T extends IFace> T getFace(String key)
{
return (T) faces.get(key);
}
It is not type safe. You are upcasting here so if you cast to an incompatible derived class you will come across an error at some point.
For example if A_Face and B_Face both extend IFace. You might at some point be casting a B_Face as an A_Face which is not type safe.
Look at the extreme case. Lets say IFace is acutally Object, your code then looks like this:
static Map<String, Object> myMap = new HashMap<>();
public static void main(String[] args) throws Exception {
myMap.put("ONE", 1);
myMap.put("TWO", "TWO");
myMap.put("THREE", new Date());
final Calendar calendar1 = getThing("ONE");
final Calendar calendar2 = getThing("TWO");
final Calendar calendar3 = getThing("THREE");
}
public static <T> T getThing(String key) {
return (T) myMap.get(key);
}
So you are putting at class than extends Object into your Map (so any class).
But, when you call getThing you are doing an implicit cast to your desired type. It should be fairly obvious that I can call getThing with any class also and it will blindly attempt to cast to it.
In the above example I am putting some things into my Map and then attempting to retrieve them all as Calendars.
A classic way to handle this is with a "typesafe heterogeneous container":
Map<Class<?>, IFace> faces;
public <T extends IFace> T getFace(Class<T> key) {
return t.cast(faces.get(key));
}
You use the class of the interface as a key, rather than a string, and then you can use the class passed as a key to safely cast the return value to the right type.
Related
For example, say I want a Map<Class<?>, List<?>>, so I can put in a class and get out a list of that type - is there something I can replace the question marks with to make that happen?
You can do the trick if you delegate type check to the method:
private class TypedMap {
private Map<Class<?>, List<?>> map = new HashMap<>();
public <T> void put(Class<T> key, List<T> value) {
map.put(key, value);
}
#SupressWarnings("unchecked")
public <T> List<T> get(Class<T> clazz) {
return (List<T>) map.get(clazz);
}
}
Wildcard ? in map declaration does not ensure that key Class<?> and value List<?> would be of the same type. Method put() ensures that. You can not declare map as Map<Class<T>, List<T>> if your class is not generic - that's why you have to use a method.
Get method is unchecked. The cast is safe if entries are added with put() method. (There's still a problem with raw types - but this is unavoidable)
You can add more methods to TypedMap class, but remember about this restrictions.
public static void main(String[] args) {
TypedMap map = new TypedMap();
List<Cat> cats = new ArrayList<>();
List<Dog> dogs = new ArrayList<>();
adder.put(Cat.class, cats);
adder.put(Dog.class, dogs);
adder.put(Cat.class, dogs); // compilation error
}
Java doesn't completely enforce this, but one way to at least get a warning about it, is by using encapsulation:
public class MyClass {
// private, private, private
private Map<Class<?>, List<?>> myMap;
public <T> void put(Class<T> clazz, List<T> list) { // both must have the same T.
myMap.put(clazz, list);
}
...
}
You can still break this by doing something like:
MyClass mc = new MyClass();
Class c = Main.class;
List<String> l = new ArrayList<String>();
mc.put(c, l);
But you'll at least get a warning about unchecked conversion of c to Class<String>. And the unchecked invocation of MyClass::put
Not sure what you're trying to accomplish here, but Map<Class<T>, List<T>> would be the closest thing. The T is one single class type, though, so you can't put multiple classes into one Map.
You'll get a ClassCastException if you try to put objects of different classes into the same Map.
I apologize in advanced for my lack of knowledge in generics... I am trying to understand how generics work and I am not sure what it is but I am missing a key part that is eluding me so hopefully someone can clarify a little more and get me over this hump.
BaseDtoUtil.mapToDto(map, OcrImageDocDto.class); //OcrImageDocDto extends DtoUtils
//This method is in class BaseDtoUtil
public static <T> List<T> mapToDto(Map<String, Object> map, Class<? extends DtoUtils> klass) throws SparkSQLException{
T obj = (T)klass.newInstance();
//return a list of these
}
So I guess there are two questions I have:
First is why does it complain when I pass in OcrImageDocDto.class when the variable defined for the method call is any class? (I originally had it as (Class<?>) Doesn't that mean any class value? Obviously I am wrong but not sure what it means then.
Second question is if I send in a class value am I actually able to get that instance and return a type value back? <T> List<T>? If I am not mistaken I believe that the generic variables <T> List<T> is used for instantiations of the object. But what do they do if it is a static method?
I am a bit lost and maybe the way I understand generics is wrong... So if someone can clear these two up I think it will help a lot!
Question 1:
public static <T> List<T> mapToDto(
Map<String, Object> map,
Class<? extends DtoUtils> klass) throws SparkSQLException{
T obj = (T)klass.newInstance();
...
You don't know that klass.newInstance() can be cast to a T - all you know is that it is an instance of DtoUtils (or a subclass).
As such, you can either change to use:
DtoUtils obj = klass.newInstance();
or constrain T to extend DtoUtils:
public static <T extends DtoUtils> List<T> mapToDto(
Map<String, Object> map,
Class<? extends T> klass) throws SparkSQLException{
T obj = klass.newInstance();
...
Question 2:
Yes, because you have an actual instance of the class. You would not be able to create an instance without that (or some other object which can provide instances of T), because of type erasure.
This would not work:
public static <T extends DtoUtils> List<T> mapToDto(
Map<String, Object> map) throws SparkSQLException{
T obj = new T(); // Compiler error.
Something like the following works just fine:
T obj = klass.newInstance();
List<T> list = new ArrayList<>();
list.add(obj);
return list;
I need to store one instance of objects for some classes.
I'd like to create a generic map like this:
<T> Map<Class<T>, T> objects;
But it's not possible and
Map<Class<?>, ?> objects;
Is not what I want because I would like to avoid casts:
MyClass c = (MyClass)objects.get(MyClass.class);
Also, I'd like Java to forbid me to do this:
objects.put(MyClass.class, new MyClass2());
One solution is to create a custom map so I would have a generic parameter <T>, but is there a solution without creating a new class?
The standard Map interface is not suitable for such scenario. However you can implement your own class which parameterizes get and put methods delegating them to the private Map field:
public class ClassObjectMap {
private Map<Class<?>, Object> map = new HashMap<>();
public <T> void put(Class<T> clazz, T value) {
assert clazz.isInstance(value);
map.put(clazz, value);
}
public <T> T get(Class<T> clazz) {
return clazz.cast(map.get(clazz));
}
}
Adding other methods like remove() or size() is not very hard as well.
If you can, instead of using the map directly, put in these methods to access the map always. That way you put the casting into the method and you dont see it anywhere else.
private Map<Class<?>, Object> map = new HashMap<Class<?>, Object>();
private <T> T getMapValue(Class<T> key){
return (T) map.get(key);
}
private <T> void putMapValue(Class<T> key, T value){
map.put(key, value);
}
And also it will forbid you from doing
putMapValue(MyClass.class, new MyClass2());
I am trying to program a kind of registry for objects of different classes.
I have the following:
public interface DbObject{
void setId(long id);
Long getId();
}
A prototypic class implementing this interface would be the following:
public class BasicDbObject implements DbObject{
private long id=null;
void setId(long id){
this.id=id;
}
Long getId(){
return id;
}
}
I want to build various different Implementations of this Interface.
And I want to be able to have a Map object, that maps from each implementing class to a Map of instances.
Something like this:
Map <Class<C implements DbObject> , Map<Long, C>> registry = new TreeMap/HashMap/SomeOtherKindOfMap (...)
I know I could do something like
Map <String,Map<Long,DbObjects>> registry = new ...
But this way I would have to write some more code for determining names, comparing classes and so on. Is there an easier way to accomplish this?
So what I want to know: is it possible to have class objects as keys in a tree map?
What would be the syntax to declare a map object, that maps from implementing classes C to a map objects each mapping from a long object (the id) to instances of C?
I want to be able to do requests like the following:
BasicObject bo = registry.get(BasicObject.class).get(42);
assuing id did
BasicObject bo=new BasicObject(...);
innerMap = new SomeMap<Long,BasicObject>();
innerMap.put(42,bo);
registry.put(BasicObject.class,innerMap);
before.
Please tell me, if this still is not clear, I have difficulties to explain, since english is not my mother tongue.
Thank you in advance.
Edit:
It turns out, i can do something very close to what I want, when defining a generic class around the map:
public class ObjectRegistry <T extends DbObject>{
private HashMap<Class<T>, TreeMap<Long,T>> registry=null;
ObjectRegistry(){
registry=new HashMap<Class<T>, TreeMap<Long,T>>();
}
public void register(T dbObject){
TreeMap<Long, T> map = registry.get(dbObject.getClass());
if (map==null){
map=new TreeMap<Long,T>();
registry.put((Class<T>) dbObject.getClass(),map);
}
map.put(dbObject.getId(),dbObject);
}
public <T extends DbObject>T get(Class<T> objectClass,long id){
TreeMap<Long, T> map = (TreeMap<Long, T>) registry.get(objectClass);
if (map != null){
return map.get(id);
}
return null;
}
public TreeMap<Long,T> getAll(Class<T> dbObjectClass) {
return registry.get(dbObjectClass);
}
}
I use a TreeMap for the inner mappings since I want to easily return Class instances sorted by id.
So the refined question is:
Is there a way to do this, without the <T extends DbObject> clause in the Class head?
Edit 2:
Thinking through it again, it turns out that John's answer is exactly the solution to this.
Here is my final code:
HashMap<Class<? extends DbObject>, TreeMap<Long, ? extends DbObject>> registry = null;
public <T extends DbObject> T get(Class<T> clazz, long id) {
TreeMap<Long, T> map = (TreeMap<Long, T>) registry.get(clazz);
if (map != null) {
return map.get(id);
}
return null;
}
public <T extends DbObject> void register(T dbObject) {
TreeMap<Long, T> map = (TreeMap<Long, T>) registry.get(dbObject.getClass());
if (map == null) {
map = new TreeMap<Long, T>();
registry.put((Class<T>) dbObject.getClass(), map);
}
map.put(dbObject.getId(), dbObject);
}
public <T extends DbObject> TreeMap<Long, T> getAll(Class<T> dbObjectClass) {
return (TreeMap<Long, T>) registry.get(dbObjectClass);
}
It does not need the <T extends DbObject> clause in the Class head.
So what I want to know: is it possible to have class objects as keys in a tree map?
TreeMap depends on there being a total order over the key space, as established by the key type having a natural order (by implementing Comparable) or by a separate Comparator object that you provide. Classes do not have a natural order. It is conceivable that you could write a suitable Comparator, but that seems very contrived to me.
But why do you specifically need a TreeMap? You didn't describe any requirement that would not be at least as well addressed by any other kind of Map. In particular, I almost always find HashMap to be a better choice, and I don't see any reason why it would be unsuitable in this one. It can certainly have objects of type Class as keys.
Moreover, if indeed you don't need any particular implementation, then you are best off declaring the type simply as a Map. That way you can actually provide any Map implementation, and even change which one you do provide if you ever discover a reason to do so.
What would be the syntax to declare a map object, that maps from implementing classes C to a map objects each mapping from a long object (the id) to instances of C?
You ask that the constraints on the type of each value be dependent on the type of the associated key, but there is no way to declare a type that enforces such a relationship. Whether a particular key or a particular value is appropriate for the Map is a function of the type of the map alone, not of each others' type.
You can write generic methods around access to your map that provide the appearance of what you want, but the data retrieval methods will need to cast. For example:
Map<Class<? extends DbObject>, Map<Long, ? extends DbObject>> registry = /*...*/;
<T extends DbObject> Map<Long, T> getRegistryMap(Class<T> clazz) {
return (Map<Long, T>) registry.get(clazz);
}
<T extends DbObject> T get(Class<T> clazz, Long id) {
Map<Long, T> map = getRegistryMap(clazz);
return (map == null) ? null : map.get(id);
}
<T extends DbObject> T put(Class<T> clazz, Long id, T obj) {
Map<Long, T> map = getRegistryMap(clazz);
if (map == null) {
map = new HashMap<>();
registry.put(clazz, map);
}
return map.put(id, obj);
}
Updated to add:
So the refined question is: Is there a way to do this, without the <T extends DbObject> clause in the Class head?
Yes, what I already wrote. Just slap a plain class declaration around it. You do not need a generic class to have generic methods. In fact, the two are orthogonal. Regular methods of a generic class can use that class's type parameters. That does not make them generic methods. A method is generic if it declares its own type parameter(s), as mine above do. Your get() method also does that, and it is important to understand that the type parameter <T> you declare explicitly in the method signature shadows the class's type parameter of the same name: it is a different T.
I want to create a kind of in-memory database into which I can put objects of any type that extends EntityProxy. I want to index every object according to its type and id number (which every EntityProxy has - something simple like creating a HashMap for each type.
I could implement this manually with something like
public void put(Object o)
{
if (o instanceof Car)
carIndex.put(o.getId(), o);
else if (o instanceof Bus)
busIndex.put(o.getId(), o);
...
}
But of course I don't want to. I want something more like
public void <T extends EntityProxy> put(T o)
{
indexMap.get(o.class).put(o.getId(), o);
}
I just don't quit have the generics vocabulary to do this. I don't know what the question mark means in template definitions, really - will something like
HashMap<Class<?>, HashMap<Long, EntityProxy>> indexMap
work?
That map is OK; if you really need to add a tiny bit of constraint, just try:
Map<Class<? extends EntityProxy>, HashMap<Long, EntityProxy>> indexMap;
This would make sure the key class can only be an EntityProxy.class or subclass.
You can think of the question mark as some "anything", but anonymous. So <?> means really anything -- any Object, <? extends EntityProxy> means anything that fulfils this condition (passes the "instanceof EntityProxy" test).
The type safety here is less than desired, as you can still put anything as key and anything in that map. I mean, you can legally put this in the map:
indexMap.put(EntityProxy1.class, new HashMap<Long, EntityProxy2>());
(assuming EntityProxy1 and EntityProxy2 are both subclasses of EntityProxy) since there's no correlation between the key and the value. To enforce that, the put() method of the map would need to be declared like this:
<T extends EntityProxy> put(Class<T> key, HashMap<Long, T> value);
T is pretty much like ? but the main difference is that it provides you with a name that you can refer to it in that context.
So, if I said that ? stands for "anything", I would say T stands for "something", as you can refer to that something once you declare it.
But you would need a custom data structure for this, as java.util.Map does not provide this kind of constraint. If you're using it as shown in your code sample, I don't think you really need these enforcements.
Note that in this example I used List. You could easily replace this with your Collection class of choice and adapt accordingly.
public interface Classifiable {
String classification();
}
public abstract class Automobile implements Classifiable {
// String classification defined in child classes
}
public class Car extends Automobile {
public String classification() { return "Car"; }
}
public class Bus extends Automobile {
public String classification() { return "Bus"; }
}
public class AutoMap {
Map<String,List<Automobile>> theMap = new Map<String,List<Automobile>>();
public AutoMap() { }
public void add(Automobile a) {
String type = a.classification();
List<Automobile> list = theMap.get(type);
if(list == null) {
list = new LinkedList<Automobile>();
theMap.put(type,list);
}
list.add(a);
}
public List<Automobile> getAutosOfType(String type){
return theMap.get(type);
}
}
public static void main(String[] args) {
List<Automobile> autos = getRandomAutos(); // defined somewhere? :)
AutoMap theAutomap = new AutoMap();
}
If you don't mind using the class name, it's as simple as:
public void <T extends EntityProxy> put(T o)
{
HashMap map = indexMap.get(o.getClass().getName());
if (map == null)
{
map = new HashMap();
indexMap.put(o.getClass().getName(), map);
}
map.put(o.getId(), o);
}
This code will create the required sub-hashmaps as you go along.
If you use getClass().getName() you get names on the form com.mypackage.Bus. If you're willing to handle name collisions and only want the simple name (in this case "Bus"), use getClass().getSimpleName() instead.
Try:
Map<Class<?>, Map<Long, EntityProxy>> indexMap = new HashMap<Class<?>, Map<Long, EntityProxy>>();
and
public void put(EntityProxy entityProxy){ // generics don't add anything here
...
(I didn't test it)