Is there any shorthand way of defining and using generic definitions without having to keep repeating a particular generic description such that if there is a change I don't have to change all definitions/usages though out the codebase for example is something like this possible:
Typedef myGenDef = < Object1, Object2 >;
HashMap< myGenDef > hm = new HashMap< myGenDef >();
for (Entry< myGenDef > ent : hm..entrySet())
{
.
.
.
}
There's the pseudo-typedef antipattern...
class StringList extends ArrayList<String> { }
Good stuff, drink up! ;-)
As the article notes, this technique has some serious issues, primarily that this "typedef" is actually a separate class and thus cannot be used interchangeably with either the type it extends or other similarly defined types.
In a generic method, you can use a limited form of type inferrence to avoid some repetitions.
Example: if you have the function
<K, V> Map<K, V> getSomething() {
//...
}
you can use:
final Map<String, Object> something = getsomething();
instead of:
final Map<String, Object> something = this.<String, Object>getsomething();
Use Factory Pattern for creation of Generics:
Method Sample:
public Map<String, Integer> createGenMap(){
return new HashMap<String,Integer>();
}
The pseudo-typedef antipattern mentioned by Shog9 would work - though it's not recommended to use an ANTIPATTERN - but it does not address your intentions. The goal of pseudo-typedef is to reduce clutter in declaration and improve readability.
What you want is to be able to replace a group of generics declarations by one single trade. I think you have to stop and think: "in witch ways is it valuable?". I mean, I can't think of a scenario where you would need this. Imagine class A:
class A {
private Map<String, Integer> values = new HashMap<String, Integer>();
}
Imagine now that I want to change the 'values' field to a Map. Why would exist many other fields scattered through the code that needs the same change? As for the operations that uses 'values' a simple refactoring would be enough.
No. Though, groovy, a JVM language, is dynamically typed and would let you write:
def map = new HashMap<complicated generic expression>();
Related
I'm using Eclipse to help me clean up some code to use Java generics properly. Most of the time it's doing an excellent job of inferring types, but there are some cases where the inferred type has to be as generic as possible: Object. But Eclipse seems to be giving me an option to choose between a type of Object and a type of '?'.
So what's the difference between:
HashMap<String, ?> hash1;
and
HashMap<String, Object> hash2;
An instance of HashMap<String, String> matches Map<String, ?> but not Map<String, Object>. Say you want to write a method that accepts maps from Strings to anything: If you would write
public void foobar(Map<String, Object> ms) {
...
}
you can't supply a HashMap<String, String>. If you write
public void foobar(Map<String, ?> ms) {
...
}
it works!
A thing sometimes misunderstood in Java's generics is that List<String> is not a subtype of List<Object>. (But String[] is in fact a subtype of Object[], that's one of the reasons why generics and arrays don't mix well. (arrays in Java are covariant, generics are not, they are invariant)).
Sample:
If you'd like to write a method that accepts Lists of InputStreams and subtypes of InputStream, you'd write
public void foobar(List<? extends InputStream> ms) {
...
}
By the way: Joshua Bloch's Effective Java is an excellent resource when you'd like to understand the not so simple things in Java. (Your question above is also covered very well in the book.)
Another way to think about this problem is that
HashMap<String, ?> hash1;
is equivalent to
HashMap<String, ? extends Object> hash1;
Couple this knowledge with the "Get and Put Principle" in section (2.4) from Java Generics and Collections:
The Get and Put Principle: use an
extends wildcard when you only get
values out of a structure, use super
wildcard when you only put values into
a structure, and don't use a wildcard
when you both get and put.
and the wild card may start making more sense, hopefully.
It's easy to understand if you remember that Collection<Object> is just a generic collection that contains objects of type Object, but Collection<?> is a super type of all types of collections.
The answers above covariance cover most cases but miss one thing:
"?" is inclusive of "Object" in the class hierarchy. You could say that String is a type of Object and Object is a type of ?. Not everything matches Object, but everything matches ?.
int test1(List<?> l) {
return l.size();
}
int test2(List<Object> l) {
return l.size();
}
List<?> l1 = Lists.newArrayList();
List<Object> l2 = Lists.newArrayList();
test1(l1); // compiles because any list will work
test1(l2); // compiles because any list will work
test2(l1); // fails because a ? might not be an Object
test2(l2); // compiled because Object matches Object
You can't safely put anything into Map<String, ?>, because you don't know what type the values are supposed to be.
You can put any object into a Map<String, Object>, because the value is known to be an Object.
Declaring hash1 as a HashMap<String, ?> dictates that the variable hash1 can hold any HashMap that has a key of String and any type of value.
HashMap<String, ?> map;
map = new HashMap<String, Integer>();
map = new HashMap<String, Object>();
map = new HashMap<String, String>();
All of the above is valid, because the variable map can store any of those hash maps. That variable doesn't care what the Value type is, of the hashmap it holds.
Having a wildcard does not, however, let you put any type of object into your map. as a matter of fact, with the hash map above, you can't put anything into it using the map variable:
map.put("A", new Integer(0));
map.put("B", new Object());
map.put("C", "Some String");
All of the above method calls will result in a compile-time error because Java doesn't know what the Value type of the HashMap inside map is.
You can still get a value out of the hash map. Although you "don't know the value's type," (because you don't know what type of hash map is inside your variable), you can say that everything is a subclass of Object and, so, whatever you get out of the map will be of the type Object:
HashMap<String, Integer> myMap = new HashMap<>();// This variable is used to put things into the map.
myMap.put("ABC", 10);
HashMap<String, ?> map = myMap;
Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map).
System.out.println(output);
The above block of code will print 10 to the console.
So, to finish off, use a HashMap with wildcards when you do not care (i.e., it does not matter) what the types of the HashMap are, for example:
public static void printHashMapSize(Map<?, ?> anyMap) {
// This code doesn't care what type of HashMap is inside anyMap.
System.out.println(anyMap.size());
}
Otherwise, specify the types that you need:
public void printAThroughZ(Map<Character, ?> anyCharacterMap) {
for (int i = 'A'; i <= 'Z'; i++)
System.out.println(anyCharacterMap.get((char) i));
}
In the above method, we'd need to know that the Map's key is a Character, otherwise, we wouldn't know what type to use to get values from it. All objects have a toString() method, however, so the map can have any type of object for its values. We can still print the values.
I'm using Eclipse to help me clean up some code to use Java generics properly. Most of the time it's doing an excellent job of inferring types, but there are some cases where the inferred type has to be as generic as possible: Object. But Eclipse seems to be giving me an option to choose between a type of Object and a type of '?'.
So what's the difference between:
HashMap<String, ?> hash1;
and
HashMap<String, Object> hash2;
An instance of HashMap<String, String> matches Map<String, ?> but not Map<String, Object>. Say you want to write a method that accepts maps from Strings to anything: If you would write
public void foobar(Map<String, Object> ms) {
...
}
you can't supply a HashMap<String, String>. If you write
public void foobar(Map<String, ?> ms) {
...
}
it works!
A thing sometimes misunderstood in Java's generics is that List<String> is not a subtype of List<Object>. (But String[] is in fact a subtype of Object[], that's one of the reasons why generics and arrays don't mix well. (arrays in Java are covariant, generics are not, they are invariant)).
Sample:
If you'd like to write a method that accepts Lists of InputStreams and subtypes of InputStream, you'd write
public void foobar(List<? extends InputStream> ms) {
...
}
By the way: Joshua Bloch's Effective Java is an excellent resource when you'd like to understand the not so simple things in Java. (Your question above is also covered very well in the book.)
Another way to think about this problem is that
HashMap<String, ?> hash1;
is equivalent to
HashMap<String, ? extends Object> hash1;
Couple this knowledge with the "Get and Put Principle" in section (2.4) from Java Generics and Collections:
The Get and Put Principle: use an
extends wildcard when you only get
values out of a structure, use super
wildcard when you only put values into
a structure, and don't use a wildcard
when you both get and put.
and the wild card may start making more sense, hopefully.
It's easy to understand if you remember that Collection<Object> is just a generic collection that contains objects of type Object, but Collection<?> is a super type of all types of collections.
The answers above covariance cover most cases but miss one thing:
"?" is inclusive of "Object" in the class hierarchy. You could say that String is a type of Object and Object is a type of ?. Not everything matches Object, but everything matches ?.
int test1(List<?> l) {
return l.size();
}
int test2(List<Object> l) {
return l.size();
}
List<?> l1 = Lists.newArrayList();
List<Object> l2 = Lists.newArrayList();
test1(l1); // compiles because any list will work
test1(l2); // compiles because any list will work
test2(l1); // fails because a ? might not be an Object
test2(l2); // compiled because Object matches Object
You can't safely put anything into Map<String, ?>, because you don't know what type the values are supposed to be.
You can put any object into a Map<String, Object>, because the value is known to be an Object.
Declaring hash1 as a HashMap<String, ?> dictates that the variable hash1 can hold any HashMap that has a key of String and any type of value.
HashMap<String, ?> map;
map = new HashMap<String, Integer>();
map = new HashMap<String, Object>();
map = new HashMap<String, String>();
All of the above is valid, because the variable map can store any of those hash maps. That variable doesn't care what the Value type is, of the hashmap it holds.
Having a wildcard does not, however, let you put any type of object into your map. as a matter of fact, with the hash map above, you can't put anything into it using the map variable:
map.put("A", new Integer(0));
map.put("B", new Object());
map.put("C", "Some String");
All of the above method calls will result in a compile-time error because Java doesn't know what the Value type of the HashMap inside map is.
You can still get a value out of the hash map. Although you "don't know the value's type," (because you don't know what type of hash map is inside your variable), you can say that everything is a subclass of Object and, so, whatever you get out of the map will be of the type Object:
HashMap<String, Integer> myMap = new HashMap<>();// This variable is used to put things into the map.
myMap.put("ABC", 10);
HashMap<String, ?> map = myMap;
Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map).
System.out.println(output);
The above block of code will print 10 to the console.
So, to finish off, use a HashMap with wildcards when you do not care (i.e., it does not matter) what the types of the HashMap are, for example:
public static void printHashMapSize(Map<?, ?> anyMap) {
// This code doesn't care what type of HashMap is inside anyMap.
System.out.println(anyMap.size());
}
Otherwise, specify the types that you need:
public void printAThroughZ(Map<Character, ?> anyCharacterMap) {
for (int i = 'A'; i <= 'Z'; i++)
System.out.println(anyCharacterMap.get((char) i));
}
In the above method, we'd need to know that the Map's key is a Character, otherwise, we wouldn't know what type to use to get values from it. All objects have a toString() method, however, so the map can have any type of object for its values. We can still print the values.
Let us say we have following enums:
public enum AnimalImages {
TIGER,BEAR,WOLF;
}
public enum CarImages {
BMW,VW,AUDI;
}
Now I want to save these enum classes in a HashMap:
Map<String,Enum<?>> enumMap=new HashMap<String,Enum<?>>();
enumMap.put("AnimalImages",???????);
What I should enter instead of the question marks?
To explicitly answer to your question, you have to put the enum value as this:
Map<String,Enum<?>> enumMap=new HashMap<String,Enum<?>>();
enumMap.put("AnimalImages", AnimalImages.TIGER);
However, if you want to put all the value belonging to an enum, then you could leverage values() method and also change your map to Map<String,Enum<?>[]> so you can use this:
Map<String,Enum<?>[]> enumMap=new HashMap<String,Enum<?>[]>(); // Note Enum<?>[] array
enumMap.put("AnimalImages", AnimalImages.values());
enumMap.put("CarImages", CarImages.values());
Another approach, with a shorten signature can be something like this:
Map<String, Object> enumMap = new HashMap<String,Object>();
enumMap.put("AnimalImages", AnimalImages.values());
enumMap.put("CarImages", CarImages.values());
Another way that bali182 pointed in this comment, you could use:
Map<String, Collection<? extends Enum<?>>> enumMap = new HashMap<>();
enumMap.put("AnimalImages", Arrays.asList(AnimalImages.values()));
enumMap.put("CarImages", Arrays.asList(AnimalImages.values()));
To answer your question:
enumMap.put("AnimalImages", AnimalImages.WOLF);
enumMap.put("Cars", CarImages.AUDI);
But you can also do
Map<String, AnimalImages> enumMap = new HashMap<String, AnimalImages>();
And this way enumMap.get("AnimalImages"), and this way you won't have to type check and cast it.
If the requirement is to create a Map that contains all values of a given enum, I would use a mapping of String keys into EnumSet values:
Map<String, EnumSet<?>> enumMap = new HashMap<String, EnumSet<?>>();
enumMap.put("AnimalImages", EnumSet.allOf(AnimalImages.class));
enumMap.put("CarImages", EnumSet.allOf(CarImages.class));
for (Map.Entry<String, EnumSet<?>> e : enumMap.entrySet()) {
System.out.println(String.format("key: [%s], values: %s",
e.getKey(), e.getValue().toString()));
}
EnumSet is a specialized Set implementation, which is represented internally as a bit vector. Its sole purpose is to provide an efficient and easy way to store enums.
Is it possible to convert ConcurrentHashMap to HashMap in java ?
This is my sample program where i was converting from ConcurrentHashMap to HashMap but i was getting the following exception:
Exception in thread "main" java.lang.ClassCastException:
java.util.concurrent.ConcurrentHashMap cannot be cast to java.util.HashMap
at com.Hi.main(Hi.java:18)
My code:
package com;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Hi {
public static void main(String args[]) {
Map<String, String> conpage = new ConcurrentHashMap<String, String>();
conpage.put("1", "A");
conpage.put("2", "B");
conpage.put("3", "C");
HashMap hm = (HashMap) conpage;
System.out.println(hm.get("1"));
}
}
Map<String, String> hashMap = new HashMap<String, String>(conpage);
A ConcurrentMap (like ConcurrentHashMap) has no relationship with any AbstractMap, such has HashMap, so the solution is to create a new HashMap adding all values from ConcurrentHashMap during object creation.
Hope this helps.
A ConcurrentHashMap is not a HashMap , so you cannot perform this cast. Treat them as Map regardless of implementation.
Nevertheless , you can use Map#putAll() .
Suggested Reading:
Java - HashMap vs Map objects
What does it mean to “program to an interface”?
Use putAll() method istead of type casting like this:
HashMap hm=new HashMap<String, String>();
hm.putAll(conpage);
ConcurrentHashMap and HashMap are siblings and not Parent-Child related. Hence the cast fails.
To be more explicit in the explanation of the previous comments: consider you have three classes :
class Position {
}
class One extends Position {
String gold = "the best";
}
class Two extends Position {
String silver = "just wait next year!";
}
You cannot do the following cast (note that a cast is not a conversion: it's only a redeclaration of the type)
void showPosition() {
Position one = new One(); // this is regular since One extends Position
Two two = (Two)one; // this is impossible because one is not a two
}
If ever this cast was possible, how would you like the compiler to handle the following promblem? : there is no field called silver in one: so calling
((Two)one).silver
is impossible: returning null would be unsafe, since you wouldn't understand what's happening : since you know that the field 'silver' is initialized to the value "just wait next year!"
Java being a safe language, it doesn't allow this type of errors (actually it's a help from Java that it throws the exception since you think the type is something else that is it is).
The behvior you expect is rather a behavior proper to scripts.
A ConcurrentHashMap is still a Map. So you can create a new TreeMap like this:
ConcurrentHashMap myMap;
...
TreeMap myTreeMap = new TreeMap( myMap );
You can do so in the following way -
HashMap<String, String> map = new HashMap<String, String>(conpage);
JavaDoc.
Typically each concrete type in the Java Collection API (like HashMap, ArrayList etc.) has a constructor which takes a reference of its parent (like Map, List) and constructs a new object from it.
About the exception you are getting, it's because ConcurrentHashMap is not a subtype/supertype of HashMap, thus you are getting a ClassCastException. However, this would have worked fine -
Map<String, String> hm = (Map<String, String> ) conpage;
Actually There is no inheritance relation between HashMap and ConcurrentHashMap that's why while casting the concurrenthashmap object to hashmap its giving the ClassCastException.
In Java it is tiring to have to write:
Pair<String, String> pair = new Pair<String, String>("one", "two");
It would be nice if the types were inferred for you so you could at least do this:
Pair<String, String> pair = new Pair("one", "two");
And skip the generic params again.
You can make a static method that can cheat around it like so:
public static <T, S> Pair<T, S> new_(T one, S two) {
return new Pair<T, S>(one, two);
}
And then use it like: Pair.new_("one", "two").
Is it possible to build the type inferencing into the constructor so that hack can be avoided?
I was thinking of something like:
public <S,T> Pair(S one, T two) {
this.one = one;
this.two = two;
}
But then you run into generic type collisions. Does anyone have any thoughts?
It s common to have a helper method which will imply the types for you.
Pair<String, String> pair = Pair.of("one", "two");
then it doesn't seem like such a hack.
It would also be nice if Java had a built in Pair class. ;)
In Java 7 you can use the "diamond" operator:
List<String> strings = new ArrayList<>();
Map<String, Int> map = new HashMap<>();
Map<String, List<String>> lolmap = new HashMap<>();
Here is the JSR which defines it: JSR334
Example using the Diamond operator and some other Java7 features: New language features in Java 7
The problem is that the declaration of a variable of a particular type, and the instantiation of an object to be referenced by that variable, are two different operations. Strings are generally hard to mistake, but let's say you were setting up pairs of numbers (maybe X-Y coordinates):
Pair<float,float> myCoords = new Pair(3,4);
This brings up a quandary; you're declaring a Pair<float, float>, but assigning an object that would probably be inferred as Pair<int,int>, unless the compiler were given enough intelligence to take the type of the variable being set into account when inferring the instantiated object's generic types (highly unlikely). Java generics, AFAIK, are not covariant, and even if they were, int does not inherit from float. I don't know of a non-duck-typed language that would handle a situation like this correctly.
I know it's tiring but that way you ensure type safety of your code. This code is a legal Java code (despite of the warning from the compiler):
import java.util.*;
public class Main {
public static void main(String[] args) {
ArrayList a = new ArrayList();
a.add(1);
ArrayList<String> b = a;
System.out.println(b.get(0));
}
}
But the compiler cannot infer the type now, yet a is assignment compatible with b. Try running it, and a runtime error would occur.
I prefer things to be explicit. When things are inferred, they can be inferred incorrectly, or the method can be used incorrectly without any notice because the system infers how to handle the incorrect arguments.