I am trying to extend a solution to the problem posted here.
The castList method shared there works greats for non-generic types.
public static <T> List<T> castList(Object obj, Class<T> clazz)
{
List<T> result = new ArrayList<T>();
if(obj instanceof List<?>)
{
for (Object o : (List<?>) obj)
{
result.add(clazz.cast(o));
}
return result;
}
return null;
}
The issue we are having is trying to use this same idea when the return type is not something simple like a list of strings, but when the list is itself of another generic, like a list of maps.
List<Map<String, Object>> list = (List<Map<String, Object>>) out.get("o_cursor");
Trying to use castList as follows produces type mismatch: cannot convert from List<Map> to List<Map<String, Object>>. Is there a way to extend castList to do this?
List<Map<String, Object>> list = castList(out.get("o_cursor"), Map.class);
I attempted a modified version specific to lists of maps, but still get an error "The method castMapList(Object, Class<Map<K,V>>) is not applicable for the arguments
(Object, Class<Map>)" which seems like a different flavor of the same error.
public static <K, V> List<Map<K, V>> castMapList(Object object, Class<Map<K, V>> clazz) {
if (object instanceof List<?>) {
List <Map<K, V>> result = new ArrayList<Map<K, V>>();
for (Object o: (List<?>) object) {
result.add(clazz.cast(o));
}
return result;
}
return null;
}
Because of erasure, there is:
No way to have a Class<Map<K, V>>.
No built-in way to check at run-time that every particular Map has some particular K and V.
That is:
As you found out, every generic class has a corresponding raw type Class object, e.g. Map.class is a Class<Map>.
Something like obj instanceof Map<K, V> is a compiler error and Class objects do not check generic arguments with e.g. isInstance and cast.
Point-being, you can't validate generics at run-time without some extra work.
You can still program around it, depending on what you're doing. So for example, one simple way is just to rebuild each Map, the same way you rebuild the List:
static <K, V> List<Map<K, V>> castMapList(
Object obj, Class<K> keyType, Class<V> valueType) {
if (obj instanceof List<?>) {
List<Map<K, V>> result = new ArrayList<>();
for (Object element : (List<?>) obj) {
if (element instanceof Map<?, ?>) {
Map<K, V> map = new HashMap<>();
for (Map.Entry<?, ?> entry : (Map<?, ?>) element) {
map.put(keyType.cast(entry.getKey()),
valueType.cast(entry.getValue());
}
result.add(map);
} else {
// do something that you want?
// personally I would throw ClassCastExceptions
// but you seem to be returning null
}
}
return result;
}
return null;
}
Also, for example, it looks kind of like you seem to be doing some sort of deserialization, and you can bake the Class right in to the Map that gets serialized:
public class MyCheckedMap<K, V>
extends SomeMapImplementation<K, V>
implements Serializable {
private static final long serialVersionUID = 1L;
private final Class<K> keyType;
private final Class<V> valueType;
public MyCheckedMap(Class<K> keyType, Class<V> valueType) {
this.keyType = Objects.requireNonNull(keyType);
this.valueType = Objects.requireNonNull(valueType);
}
#SuppressWarnings("unchecked")
public static <K, V> MyCheckedMap<K, V> cast(
Object obj, Class<K> keyType, Class<V> valueType) {
if (!(obj instanceof MyCheckedMap<?, ?>))
throw new ClassCastException(obj.getClass().toString());
MyCheckedMap<?, ?> map = (MyCheckedMap<?, ?>) obj;
validate(keyType, map.keyType);
validate(valueType, map.valueType);
return (MyCheckedMap<K, V>) obj;
}
private static void validate(Class<?> lhs, Class<?> rhs) {
if (lhs == rhs)
return;
throw new ClassCastException(String.format("%s != %s", lhs, rhs));
}
}
You have to write some boiler-plate code for each List, Map, etc., but probably not much more than you seem to be doing.
There are also some other ways to approach this sort of problem, like Neal Gafter's Super Type Tokens and Guava's TypeToken, which is a derivative of Neal Gafter's idea. These essentially use reflection to achieve something like MyCheckedMap.
I'm not sure whether or not having unchecked warnings is an issue for you or not. For the map, you could do the following:
public <K, V> List<Map<K, V>> castMapList(Object object, Class<K> class1, Class<V> class2) {
if (object instanceof List<?>) {
List<Map<K, V>> result = new ArrayList<>();
for (Map<K, V> o : (List<Map<K, V>>) object) { //Unchecked cast
result.add(o);
}
return result;
}
return null;
}
or
for (Object o : (List<?>) object) {
if (o instanceof Map<?, ?>) {
result.add((Map<K, V>) o); //Unchecked cast
}
}
I don't think having a suppress warning is the worst case for you, since even in the cast method from Class.java, they do it. You must be sure that the list contains maps though.
This is what I came up with. Thanks again!
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CastUtility {
public static <T> List<T> castList(Object object, Class<T> clazz) {
if (object == null) {
return null;
}
if (object instanceof List<?>) {
List<T> result = new ArrayList<T>();
for (Object o : (List<?>) object) {
result.add(clazz.cast(o));
}
return result;
}
throw new ClassCastException();
}
public static <K, V> Map<K, V> castMap(Object object, Class<K> keyType,
Class<V> valueType) {
if (object == null) {
return null;
}
if (object instanceof Map<?, ?>) {
Map<K, V> result = new HashMap<K, V>();
for (Map.Entry<?, ?> entry : ((Map<?, ?>) object).entrySet()) {
result.put(keyType.cast(entry.getKey()),
valueType.cast(entry.getValue()));
}
return result;
}
throw new ClassCastException();
}
public static <K, V> List<Map<K, V>> castMapList(Object object,
Class<K> keyType, Class<V> valueType) {
if (object == null) {
return null;
}
if (object instanceof List<?>) {
List<Map<K, V>> result = new ArrayList<Map<K, V>>();
for (Object o : (List<?>) object) {
result.add(castMap(o, keyType, valueType));
}
return result;
}
throw new ClassCastException();
}
}
Related
I have a following interface
public interface Splitter<T, V> {
V[] split(T arg);
}
Following is the factory method implementation which I am using to get Splitter Implementation.
Factory Method Implementation
public static <T, V> Splitter<T, V> getSplitter(Class<T> key1, Class<V> key2) {
if (key1 == Company.class && key2 == Department.class)
return (Splitter<T, V>) new CompanySplitterImpl();
// more cases
}
Following is my call at client side which compiles fine
Splitter<Company, Department> split = getSplitter(Company.class, Department.class);
I want to avoid tight coupling of client side code with the implementation. Is there a way to avoid hardcoded Type params i.e. avoiding use Company and Department (Splitter<Company, Department>) at callee side and use some variable instead? Is there a way out through which they can be loaded from some external property file?
FYI : I am not sure about its feasibility in Java?
One thing you could do is have you factory not know anything about concrete implementations and instead have themselves register with it (or contain a predefined list) and ask each implementation if it can handle the types or not. For example, given a predefined list like your example above:
public class SplitterFactory {
private Set<Splitter> splitters = new HashSet<>() {{
add(new CompanySplitterImpl());
}};
public static <T, V> Splitter<T, V> getSplitter(Class<T> key1, Class<V> key2)
{
for (Splitter splitter : splitters) {
if (splitter.canAccept(key1, key2)) {
return splitter;
}
// no matched splitter
}
}
Obviously this is a very naive solution and you could implement the lookup more efficiently. If you don't know your types at compile time, you could also have a registration mechanism with the factory to include new ones at runtime. Because the Splitter itself is now responsible for reporting what types it can handle it's fully extensible.
You could make a simple map class that you can list them up to:
public final class SplitterMap {
private final List<SplitterType<?, ?>> list = new ArrayList<>();
private class SplitterType<T, V> {
private final Class<T> key1;
private final Class<V> key2;
private final Class<? extends Splitter<T, V>> clazz;
private SplitterType(Class<?> key1, Class<?> key2, Class<? extends Splitter<T, V> clazz) {
this.key1 = key1;
this.key2 = key2;
this.clazz = clazz;
}
private boolean matches(Class<?> key1, Class<?> key2) {
return this.key1 == key1 && this.key2 == key2;
}
}
public <T, V> void put(Class<T> key1, Class<V> key2, Class<? extends Splitter<T, V> clazz) {
list.add(new SplitterType<T, V>(key1, key2, clazz));
}
public <T, V> Splitter<T, V> get(Class<T> key1, Class<V> key2) {
for (SplitterType<?, ?> type : list) {
if (type.matches(key1, key2)) {
try {
return ((SplitterType<T, V>) type).clazz.newInstance();
} catch (Exception e) {
}
}
}
return null; // not found
}
}
Then you could just do:
SplitterMap map = new SplitterMap();
map.put(Company.class, Department.class, CompanySplitterImpl.class);
Splitter<Company, Department> splitter = map.get(Company.class, Department.class);
Not a good way but one way would be:
String companyClass = "Company";
String departmentClass = "Department";
Splitter split = getSplitter(Class.forName(companyClass), Class.forName(departmentClass));//raw splitter
System.out.println(split.split(new Company()));//you could use reflection here to create instance from companyClass String.
Firstly I assume you want something like
Splitter<Company, Department> s = Splitters.getSplitter()
Which is not posible without reflection, because of
type erasure
return type overloading not available in java yet
Secondly you're abusing FactoryMethod pattern. Which should look more like this:
interface Splitter<T, V> {
V[] split(T arg);
}
interface SplitterFactory {
<T, V> Splitter<T, V> getSplitter();
}
class CompanySplitterFactory implements SplitterFactory {
#Override
public Splitter<Company, Department> getSplitter() {
return new CompanySplitterImpl();
}
}
Let's say I have the following Map which is created using Guava's library: (List<Integer> is also immutable)
Map<String, List<Integer>> map = ImmutableMap.builder()...
I pass this map to a class where I want to create a mutable copy of it and modify it. It is of course possible to do it manually, but is there a way to convert a nested immutable collection back to a mutable one?
As pointed out, I'd use an ImmutableListMultimap<String, Integer> instead of a ImmutableMap<String, ImmutableList<Integer>>.
Then if you want a mutable copy, you can just pass the immutable multimap to the create static factory method on one of the mutable ListMultimap implementations (ArrayListMultimap or LinkedListMultimap).
Here is my solution. There'e quite a lot of code required to set it up, but once it's done it's really easy to use.
public class Main {
// UnaryOperator and identity are in Java 8.
// I include them here in case you are using an earlier version.
static interface UnaryOperator<T> {
T apply(T t);
}
static <T> UnaryOperator<T> identity() {
return new UnaryOperator<T>() {
#Override
public T apply(T t) {
return t;
}
};
}
// This unary operator turns any List into an ArrayList.
static <E> UnaryOperator<List<E>> arrayList(final UnaryOperator<E> op) {
return new UnaryOperator<List<E>>() {
#Override
public List<E> apply(List<E> list) {
List<E> temp = new ArrayList<E>();
for (E e : list)
temp.add(op.apply(e));
return temp;
}
};
}
// This unary operator turns any Set into a HashSet.
static <E> UnaryOperator<Set<E>> hashSet(final UnaryOperator<E> op) {
return new UnaryOperator<Set<E>>() {
#Override
public Set<E> apply(Set<E> set) {
Set<E> temp = new HashSet<E>();
for (E e : set)
temp.add(op.apply(e));
return temp;
}
};
}
// This unary operator turns any Map into a HashMap.
static <K, V> UnaryOperator<Map<K, V>> hashMap(final UnaryOperator<K> op1, final UnaryOperator<V> op2) {
return new UnaryOperator<Map<K, V>>() {
#Override
public Map<K, V> apply(Map<K, V> map) {
Map<K, V> temp = new HashMap<K, V>();
for (Map.Entry<K, V> entry : map.entrySet())
temp.put(op1.apply(entry.getKey()), op2.apply(entry.getValue()));
return temp;
}
};
}
public static void main(String[] args) {
// In this example I will first create an unmodifiable collection of unmodifiable collections.
Map<String, List<Set<Integer>>> map = new HashMap<String, List<Set<Integer>>>();
map.put("Example", Collections.unmodifiableList(Arrays.asList(Collections.unmodifiableSet(new HashSet<Integer>(Arrays.asList(1, 2, 3))))));
map = Collections.unmodifiableMap(map);
// Now I will make it mutable in one line!
map = hashMap(Main.<String>identity(), arrayList(hashSet(Main.<Integer>identity()))).apply(map);
}
}
For the purpose of learning I try to make a MultiMap implementation. (and for avoiding relying on other libraries for a library I make).
It doesn't have to be perfect.
At the moment I have this:
class MultiMap<k, v> implements Map {
HashMap<k, List<v>> hMap = new HashMap<k, List<v>>();
public MultiMap () {
}
Followed by all #Override methods from Map.
One is like this:
#Override
public Object get(Object o) {
return hMap.get(o);
}
I have problems with this one:
#Override
public Object put(Object o, Object o2) {
// will return a list
Object toReturn = get(o);
if(hMap.containsValue(o)) {
// is this even possible?
(List<v>)(List<?>)get(o); // <<< problem: "Syntax error on token(s), misplaced construct(s)"
// ^ next .add(o2);
}
// etc.
return toReturn;
}
Is it possible to get a List out of the get method?
You define class like this class MultiMap<k, v> implements Map<k, List<v>>
and then add new method public List<v> put(k key, v value) like that.
public List<v> put(k key, v value) {
List<v> list = get(key);
if(list == null)
list = new ArrayList<>();
list.add(value);
return list;
}
In Java, I want to add a getOrAdd method to a regular map, just like putIfAbsent on a ConcurrentHashMap.
Furthermore, for a certain key I want to store a list of items. Here's my attempt:
public class ListMap<K, V> extends HashMap<K, V> {
private HashMap<K, List<V>> map;
public ListMap() {
map = new HashMap<K, List<V>>();
}
public List<V> getOrAdd(K key) {
if (map.containsKey(key)) {
return map.get(key);
} else {
List<V> l = new ArrayList<V>();
map.put(key, l);
return l;
}
}
}
However, if someone wanted to iterate over a ListMap, he would need to cast the values explictly.
ListMap<Integer, MyClass> listMap = new ListMap<Integer, MyClass>();
for (Map.Entry<Integer, MyClass> entry : listMap.entrySet()) {
List<MyClass> val = (List<MyClass>) entry.getValue();
}
Is there a way of extending the HashMap class by some methods without creating a subclass? ( I've seen this in C#)
How can the ListMap class be modified such that one can get a ListMaps's value (List) without casting?
Instance of your class will be also HashMap so you don't need to, or even shouldn't add another field just to support getOrAdd method because other inherited and not overridden methods will not be referring to map field but to this instance.
So instead of adding separate field
private HashMap<K, List<V>> map;
change extending type of your ListMap to
public class ListMap<K, V> extends HashMap<K, List<V>>
^^^^^^^
and change your getOrAdd method to not use map field but this
public List<V> getOrAdd(K key) {
if (containsKey(key)) {
return get(key);
} else {
List<V> l = new ArrayList<V>();
put(key, l);
return l;
}
}
This change will let you use your map like
ListMap<Integer, String> listMap = new ListMap<Integer, String>();
for (Map.Entry<Integer, List<String>> entry : listMap.entrySet()) {
List<String> val = entry.getValue();//NO CASTING NEEDED
}
You can just extend HashMap like this:
public class ListMap<K, V> extends HashMap<K, List<V>> {
...
}
Background
An existing system creates a plethora of HashMap instances via its Generics class:
import java.util.Map;
import java.util.HashMap;
public class Generics {
public static <K,V> Map<K, V> newMap() {
return new HashMap<K,V>();
}
public static void main( String args[] ) {
Map<String, String> map = newMap();
}
}
This is the single point of creation for all instances of classes that implement the Map interface. We would like the ability to change the map implementation without recompiling the application. This would allow us to use Trove's THashMap, for example, to optimize the application.
Problem
The software cannot be bundled with Trove's THashMap due to licensing conditions. As such, it would be great if there was a way to specify the name of the map to instantiate at runtime (for those people who have no such licensing restrictions). For example:
import java.util.Map;
import java.util.HashMap;
import gnu.trove.map.hash.THashMap;
public class Generics {
private String mapClassName = "java.util.HashMap";
#SuppressWarnings("unchecked")
public <K,V> Map<K,V> newMap() {
Map<K,V> map;
try {
Class<? extends Map<K,V>> c = (Class<Map<K,V>>)Class.forName(
getMapClassName() ).asSubclass( Map.class );
map = c.newInstance();
}
catch( Exception e ) {
map = new HashMap<K,V>();
}
return map;
}
protected String getMapClassName() {
return this.mapClassName;
}
protected void setMapClassName( String s ) {
this.mapClassName = s;
}
public static void main( String args[] ) {
Generics g = new Generics();
Map<String, String> map = g.newMap();
System.out.printf( "Class = %s\n", map.getClass().toString() );
g.setMapClassName( "gnu.trove.map.hash.THashMap" );
map = g.newMap();
System.out.printf( "Class = %s\n", map.getClass().toString() );
}
}
Question
Is there a way to avoid the #SupressWarnings annotation when compiling with -Xlint and still avoid the warnings?
Is there a way to avoid the #SuppressWarnings annotation when compiling with -Xlint and still avoid the warnings?
No. Class.forName returns a Class<?>. The only way to assign it to Class<? extends Map<K, V>> is to do an unchecked cast. Sometimes unchecked casts are necessary, and so using #SuppressWarnings("unchecked") is acceptable here (provided you document the reason well).
IMHO it would be more correct to keep a reference to Class<? extends Map<?, ?>> and then do an unchecked cast of the result of newInstance to Map<K,V>. I only say this because a Class object is a canonical representation of a raw type, so a type like Class<? extends Map<K, V>> is slightly misleading.
This is outside the scope of the question, but here's a suggested alternative for your solution:
public interface MapFactory {
<K, V> Map<K, V> newMap() throws Exception;
}
public enum HashMapFactory implements MapFactory {
INSTANCE;
#Override
public <K, V> Map<K, V> newMap() {
return new HashMap<K, V>();
}
}
public static final class DynamicMapFactory implements MapFactory {
private final Constructor<? extends Map<?, ?>> constructor;
private DynamicMapFactory(Constructor<? extends Map<?, ?>> constructor) {
this.constructor = constructor;
}
#Override
//Impl note: these checked exceptions could also be wrapped in RuntimeException
public <K, V> Map<K, V> newMap() throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
#SuppressWarnings("unchecked") //this is okay because the default ctor will return an empty map
final Map<K, V> withNarrowedTypes = (Map<K, V>)constructor.newInstance();
return withNarrowedTypes;
}
public static DynamicMapFactory make(String className) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
#SuppressWarnings("unchecked") //Class<? extends Map> can safely be cast to Class<? extends Map<?, ?>>
final Class<? extends Map<?, ?>> type =
(Class<? extends Map<?, ?>>)Class.forName(className).asSubclass(Map.class);
final Constructor<? extends Map<?, ?>> constructor = type.getDeclaredConstructor();
return new DynamicMapFactory(constructor);
}
}
public static void main(String[] args) throws Exception {
Map<String, Integer> map1 = HashMapFactory.INSTANCE.newMap();
Map<String, Integer> map2 = DynamicMapFactory.make("java.util.TreeMap").newMap();
System.out.println(map1.getClass()); //class java.util.HashMap
System.out.println(map2.getClass()); //class java.util.TreeMap
}