Generic Type Repeated - java

I have this class...
public abstract class LoadboardTable
{
protected Map<String, HashMap<HasTableFields, String>> table = new TreeMap<String, HashMap<HasTableFields, String>>();
public Set<Entry<String, HashMap<HasTableFields, String>>> getEntries()
{
return table.entrySet();
}
...
}
In other classes, I am constantly repeating the generic type. For example...
for (Entry<String, HashMap<HasTableFields, String>> entry : myTable.getEntries()){}
Set<Entry<String, HashMap<HasTableFields, String>>> entries = otherTable.getEntries();
etc, etc...
This generic type is repeated and littered all over the application. Is there a better way? If I ever decide to change the generic type of the table Map object in the LoadboardTable class, I'll be changing it everywhere else too for days. Plus it's just a huge pain to keep typing it.

There is no way to avoid the repetition, except in the constructor, since Java 7:
protected Map<String, HashMap<HasTableFields, String>> table = new TreeMap<>();
You would have better code, and less to type, if you encapsulated the HashMap and entries in well-defined classes, though. It looks like you're using objects as open data structures, instead of using them as closed objects offering behaviour and keeping their state encapsulated.

3 Advises:
Instead of using so much generics, think about classes that you actually want to implement, instead of solving everything with generics.
Use the diamond operator with Java 7.
When using eclipse you can write just "getEntries" and then press CTRL+1 and click "assign to local variable" - this will automatically create a local variable with the right type. This does not solve the problem, but will make it a bit faster to write.

public abstract class LoadboardTable<T,O>
{
protected Map<T, HashMap<O, T>> table = new TreeMap<T, HashMap<O, T>>();
public Set<Entry<T, HashMap<O, T>>> getEntries()
{
return table.entrySet();
}
}

Related

java - to set multiple value in a map

I got a scenario like the following:
Map1 - Map<String, Map<String,List<Vo>>>
Map2 - Map<String, Set<String>
Is it possible to set the same have a same key reference for the above 2 Maps like the following?
Map<String, Collection<?> mapCommon=new HashMap<String, Collection<?>();
Can anyone please give some idea about how to set this?
edit: yes same reference
You are touching here two interesting elements.
Firstly - Map does not belong to Collection. List and Set do belong, but Map is a different one even though it shares some commonalities with Lists and Sets.
Secondly - Mixing the types into one commonMap the way you are trying is doable but it should be avoided as it is generally not considered as best practice. The problem we are dealing with is caused by type erasure. Once compiler compiles the code - it does not pass any information about generic types hold by Map or Set. Effectively your Map<String, List<Vo>> becomes raw-type Map<?> in the compiled code. The problem with that is casting back original values. The compiler will not allow you to check the instance if it is Map<String, List<Vo>> or Set<String>.
The fllowing piece of code will fail:
public static void processElement(Object commonMapObjectEitherMapOrSet) {
if (commonMapObjectEitherMapOrSet instanceof Map<String, List<Vo>>) {
//...
}
}
Error: Cannot perform instanceof check against parameterized type
Map>. Use the form Map instead since further
generic type information will be erased at runtime
The possible workaround would be to forget about generics and check if the instance is a raw-type Set or Map. The code below shows how check if Object is either Map or Set.
public static void processElement(Object commonMapObjectEitherMapOrSet) {
if (commonMapObjectEitherMapOrSet instanceof Map) {
System.out.println("Got map; but types held in the map are not known due to type-erasure");
// This is where things will get messy as you will get warnings:
Map<String, List<Vo>> map = (Map<String, List<Vo>>) commonMapObjectEitherMapOrSet;
// ...
}
if (commonMapObjectEitherMapOrSet instanceof Set) {
System.out.println("Got set; but types held in the set are not known due to type-erasure");
// This is where things will get messy as you will get warnings:
Set<String> set = (Set<String>) commonMapObjectEitherMapOrSet;
// ...
}
}
The problem with the above is casting the value from your commonMap back to your desired types ie. Map<String, List<Vo>> and Set<String>. The compiler won't be able to check if the casting is correct and will issue a warning. You can technically Suppress the warning with (#SuppressWarnings("unchecked") annotation ) but this may not be the best thing to do.
At this stage - it makes sense to consider whether or not to create your own specialized class to manage different types.
Back to your original question - to answer it I am posting the code that maps things to the common map:
package stackoverflow;
import java.util.*;
class Vo {}
public class MultipleRefs {
public static void main(String[] args) {
Map<String, List<Vo>> mapVo = new HashMap<>();
Set<String> set = new HashSet<>();
Map<String, Object> commonMap = new HashMap<>();
//commonMap.put("a", Map)
commonMap.put("mapVoOne", mapVo);
commonMap.put("setOne", set);
commonMap.forEach((key, value) -> processElement(value));
}
public static void processElement(Object commonMapObject) {
if (commonMapObject instanceof Map) {
System.out.println("Got map; but types held in the map are not known due to type-erasure");
// This is where things will get messy:
Map<String, List<Vo>> map = (Map<String, List<Vo>>) commonMapObject;
System.out.println(" processElement prints map: " + map);
}
if (commonMapObject instanceof Set) {
System.out.println("Got set; but types held in the set are not known due to type-erasure");
// This is where things will get messy:
Set<String> set = (Set<String>) commonMapObject;
System.out.println(" processElement prints set: " + set);
}
}
}
If I understand you would want to have the same key to be used for various different types of values.
Why not have a new Class itself that would consists of maps, sets, whose instances could be used as values
class MyClass {
private Map<String, List<Vo>> theMap;
private Set<String> theSet;
...
... // have its own getters and setters
}
And then you can have your top level map defined like this
Map<String, MyClass> myMainMap = new HashMap<String, MyClass>();
Or as an alternative have a tuple
You can check this link further to see how that is done.
What you want to do is impossible because Set and Map do not share any common implementation or super class except Object. You can see it in the official documentation :
Javadoc Map
Javadoc Set
You could do a Map<String, Object> but I strongly not advise you to doing that. How could you know if your object is a map or a set ? It is not possible to do that properly.
In my opinion, the best solution you have is to create a new class to wrap your two collections :
public class YourWrapper {
Map<String, Map<String,List<Vo>>> a;
Map<String, Set<String> b;
// getter setter etc...
}
After that you can create your collection :
Map<String, YourWrapper> myMap = new HashMap<String, YourWrapper>();

Converting Map<String,String> to Map<String,Object>

I have Two Maps
Map<String, String> filterMap
Map<String, Object> filterMapObj
What I need is I would like to convert that Map<String, String> to Map<String, Object>.
Here I am using the code
if (filterMap != null) {
for (Entry<String, String> entry : filterMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
Object objectVal = (Object)value;
filterMapObj.put(key, objectVal);
}
}
It works fine, Is there any other ways by which I can do this without iterating through all the entries in the Map.
Instead of writing your own loop that calls put, you can putAll, which does the same thing:
filterMapObj.putAll(filterMap);
(See the Javadoc.)
And as Asanka Siriwardena points out in his/her answer, if your plan is to populate filterMapObj immediately after creating it, then you can use the constructor that does that automatically:
filterMapObj = new HashMap<>(filterMap);
But to be clear, the above are more-or-less equivalent to iterating over the map's elements: it will make your code cleaner, but if your reason for not wanting to iterate over the elements is actually a performance concern (e.g., if your map is enormous), then it's not likely to help you. Another possibility is to write:
filterMapObj = Collections.<String, Object>unmodifiableMap(filterMap);
which creates an unmodifiable "view" of filterMap. That's more restrictive, of course, in that it won't let you modify filterMapObj and filterMap independently. (filterMapObj can't be modified, and any modifications to filterMap will affect filterMapObj as well.)
You can use the wildcard operator for this.
Define filterMapObj as Map<String, ? extends Object> filterMapObj and you can directly assign the filterMap to it. You can learn about generics wildcard operator
You can simply write
Map<String, Object> filterMapObj = new HashMap<>(filterMap);
You can use putAll method to solve the problem.The Object is the father class of all objects,so you can use putAll without convert.

How do I create some variable type alias in Java

let say I have this code
Map<String, String> list = new HashMap<String, String>();
list.put("number1", "one");
list.put("number2", "two");
how can I make some "alias" the type
Map<String, String>
to something that easier to be rewritten like
// may be something like this
theNewType = HashMap<String, String>;
theNewType list = new theNewType();
list.put("number1", "one");
list.put("number2", "two");
basically my question is, how to create "alias" to some "type", so i can make it easier to write and easier when need to change the whole program code.
Thanks, and sorry if this is silly question. I'm kinda new in Java.
There are no aliases in Java. You can extend the HashMap class with your class like this:
public class TheNewType extends HashMap<String, String> {
// default constructor
public TheNewType() {
super();
}
// you need to implement the other constructors if you need
}
But keep in mind that this will be a class it won't be the same as you type HashMap<String, String>
There is no typedef equivalent in Java, and there is no common idiom for aliasing types. I suppose you could do something like
class StringMap extends HashMap<String, String> {}
but this is not common and would not be obvious to a program maintainer.
Although Java doesn't support this, you can use a generics trick to simulate it.
class Test<I extends Integer> {
<L extends Long> void x(I i, L l) {
System.out.println(
i.intValue() + ", " +
l.longValue()
);
}
}
Source: http://blog.jooq.org/2014/11/03/10-things-you-didnt-know-about-java/
The closest one could think of is to make a wrapper class like so
class NewType extends HashMap<String, String> {
public NewType() { }
}
I really wish Java had a sound type aliasing feature.
Nothing like that exists in Java. You might be able to do something with IDE templates or autocompletion, and look forward to (limited) generics type inference in Java 7.

Java generics parameters with base of the generic parameter

I am wondering if there's an elegant solution for doing this in Java (besides the obvious one - of declaring a different/explicit function. Here is the code:
private static HashMap<String, Integer> nameStringIndexMap
= new HashMap<String, Integer>();
private static HashMap<Buffer, Integer> nameBufferIndexMap
= new HashMap<Buffer, Integer>();
// and a function
private static String newName(Object object,
HashMap<Object, Integer> nameIndexMap){
....
}
The problem is that I cannot pass nameStringIndexMap or nameBufferIndexMap parameters to the function. I don't have an idea about a more elegant solution beside doing another function which explicitly wants a HashMap<String, Integer> or HashMap<Buffer, Integer> parameter.
My question is:
Can this be made in a more elegant solution/using generics or something similar?
Thank you,
Iulian
You could make your function generic too:
private static <E extends Object> String newName(E object,
HashMap<E, Integer> nameIndexMap){
....
}
This bounds the two parameters of the function together, so for a HashMap<String, Integer> you can only pass String instances as first parameter. This may or may not be what you exactly want: if you only want to get elements from the map, Jon's solution is simpler, but if you want to add this object to the map, this one is the only choice.
You want something like this:
private static String newName(Object object,
HashMap<? extends Object, Integer> nameIndexMap) {
....
}
or (as pointed out in the comments)
private static String newName(Object object,
HashMap<?, Integer> nameIndexMap) {
....
}
That will stop you from putting anything into the map, because you couldn't guarantee to get the key right - but you can get things out of the map and guarantee they'll be integers.
Note that this version doesn't make the method generic - which means it's simpler, but it doesn't provide the same type safety that Peter's version does, in that you can't guarantee that object is of the right type. Each approach has its pros and cons - use whatever is most appropriate based on the body of the method. (If you need to put an entry into the map, Peter's approach is definitely better.)

What is the difference between ? and Object in Java generics?

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.

Categories