NullPointerException thrown trying to copy Map - java

I am trying to create an XML element object and assign some attributes during construction, but I keep getting a NullPointerException thrown, with the following code:
public XML.Element newElement(String name, Map<String, String> attributes) {
return new ElementImpl(name, attributes);
}
calling
public class ElementImpl implements XML.Element {
private Map<String, String> attributes = new LinkedHashMap<String, String>();
public ElementImpl(String name, Map<String, String> attributes) {
...
this.attributes.putAll(attributes);
}
Stepping through with a debugger, it shows that "this" is null. Can anyone explain where I'm going wrong please?

I would advise you to remove the putAll method and instead assign the variable. This is the constructor, so it's the first time you put data on this instance.
Also, are you sure you don't get any errors when you create the LinkedList? At least the class I know in Java can take only one argument and you add two.
public class ElementImpl implements XML.Element {
private Map<String, String> attributes;
public ElementImpl(String name, Map<String, String> attributes) {
this.attributes = attributes;
}
}
Write this instead. I think it will work.
If the comment below is write, then you can try this.
this.attributes = new LinkedHashMap(attributes);
That way you will get your copy.

Related

Spotbugs + Java: may expose internal representation by storing an externally mutable object into QuestionPojo.map

Small question regarding a Spotbug finding I am having a hard time to fix please.
On this super simple POJO:
import java.util.Map;
public class QuestionPojo {
private final Map<String, String> map;
public QuestionPojo(Map<String, String> map) {
this.map = map;
}
public Map<String, String> getMap() {
return map;
}
#Override
public String toString() {
return "QuestionPojo{" +
"map=" + map +
'}';
}
}
I am getting flag on the map with may expose internal representation by storing an externally mutable object into QuestionPojo.map
One time on this.map = map;
Another one on the getter return map.
I tried invoking a possible clone() method, but it seems it is not supported in Map.
How do I fix this please?
Thank you
may expose internal representation by storing an externally mutable object into QuestionPojo.map
What this is telling you is that the internal state of an instance of this class can be changed outside the class.
For example:
Map<String, String> map = new HashMap<>();
map.put("hello", "world");
QuestionPojo qp = new QuestionPojo(map);
// Both of these lines change the map stored inside qp.
map.clear();
qp.getMap().put("silly", "silly");
As to whether this external change of state is important depends on the semantics of your class - in general it is undesirable, however, because it makes it hard to reason about its behavior, because the map can be changed from afar (i.e. anywhere that has a reference to the QuestionPojo, or the map).
The solution to this is defensive copying: take a copy of the map in the constructor:
public QuestionPojo(Map<String, String> map) {
this.map = new HashMap<>(map);
}
and return a copy of the map in the getter:
public Map<String, String> getMap() {
return new HashMap<>(map);
}
This means that nothing outside the class has access to the map stored inside the class.

Generics - define Map<MyClassA<?>, MyClassB<?>> ensuring both ? are the same?

I have a line of code:
private final Map<MyClassA<?>, MyClassB<?>> myMap = new HashMap<>();
Is there any way to define that map in a way that would tell the compiler that the ? in each case must be the same class?
Something like this?
private final <T> Map<MyClassA<T>, MyClassB<T>> myMap = new HashMap<>();
... which is not legal syntax?
It's just a self-learning question at this point.
FWIW, I want to add a method
public <T> MyClassB<T> getForA(MyClassA<T> a) {
return this.myMap.get(a);
}
But I get a compile error unless I can define myMap to insist that both the key and the value wrap the same type.
As you already figured out, you can't do that if key and value are different for different entries:
map.put(new MyClassA<Foo>(), new MyClassB<Foo>());
map.put(new MyClassA<Bar>(), new MyClassB<Bar>());
(I've taken this requirement from your comment)
What you can do is to write some helper methods, which enforce this constraint:
public <T> void put(MyClassA<T> key, MyClass<B> value) {
// Maybe check at runtime if the constraint is not validated?
map.put(key, value);
}
public <T> MyClassB<T> get(MyClassA<T> key) {
// This will produce an unchecked warning.
return (T) map.get(key);
}
As long as you only access the map through such helper methods (and don't use raw types), the constraint on the map will not be violated, which allows you to write type safe code.
The only part that is not typesafe are those helper methods, and that's where you have to be careful.
You can do something similar if you introduce one static inner class for the type you need. For example:
public class DoubleGenericTest<T> {
public static class MapHolder<Z> {
private final Map<MyClassA<Z>, MyClassB<Z>> myMap = new HashMap<>();
}
private final MapHolder<String> stringMap = new MapHolder<>();
private final MapHolder<Integer> integerMap = new MapHolder<>();
}
class MyClassA<X> {}
class MyClassB<Y> {}
This gives you the class you need to hang the type parameter onto. Maybe not ideal in every situation but it's the only thing I can think of.

How to use a map with a generic enum type as function return type in Java

I am using an interface method that returns the map with these key, values.
public interface IParse<T> {
Map<String, T> parse(); //T is an enum type
}
Now in the implementation classes, I use the function parse with
public class TestClass1 implements IParse
{
public Map<String, EnumType1> parse()
{
Map<String, EnumType1> map1 = new HashMap<>();
// Logic to fill the map
return map1;
}
}
public class TestClass2 implements IParse
{
public Map<String, EnumType2> parse()
{
Map<String, EnumType2> map2 = new HashMap<>();
// Logic to fill the map
return map2;
}
}
and return the map containing appropriate enum type. But eclipse seems does not like it. One of the help options shows "Infer Generic Type Arguments".
Now in the above case, how do I use properly return the map without any type casting. Is there any other way I can use the interface method with a return value a map containing values as enum class types. Please give me an example on how to do this.
Please let me know if any further details are required.

Access a HashMap from a separate jar

So I have this code in JarFile1.jar:
public static HashMap<String, Double[]> myHashMap = new HashMap<String, Double[]>();
How would I access that variable in a separate JarFile2.jar using import?
Put getter and setter to that var in jar1File:
class MyClass {
public HashMap<String, Double[]> getMyHashMap() {
return myHashMap;
}
public void setMyHashMap(HashMap<String, Double[]> myHashMap) {
this.myHashMap = myHashMap;
}
}
Once you are in jarFile2 just you the Class name, Because this is static member.
class MyClassJar2 {
public MyClassJar2() {
MyClass.getMyHashMap();
}
}
So when you will import you can access that var
Hope that help :)
Firstly, that doesn't sound like a good practice at all. It's not a good idea to allow access to an atribute of an object like a Map (you could clutter the data easily and without noticing).
If you want to access data from another class, you could implement a public method like public double[] getData(String key) and importing it in the other jar. Also, note that you need to add the first jar to the classpath while executing the second one.

Pass a Map<String, String> to a method requiring Map<String, Object>

I have a method with the following signature
public static ActionDefinition reverse(String action, Map<String, Object> args)
And I have method that returns the following:
public static Map<String, String> toMap(String value)
Is there some way I can cast the output of toMap to be used in reverse, something like:
ActionDefinition ad = reverse("Action.method", toMap("param1=value1,param2=value2"));
I'd need to do something like
(Map<String, Object>) toMap("param1=value1,param2=value2");
but I couldn't fin the way to do it
I also tried with the following method
public static Map<String, String> toMap(String value) {
Map<String, Object> source = toMap(value);
Map<String, String> map = new LinkedHashMap<String, String>();
for(Map.Entry<String, Object> entry: source.entrySet()) {
map.put(entry.getKey(), (String) entry.getValue());
}
return map;
}
but I guess that due to type erasure, I get that the method is duplicated...
any idea?
--
edit
I forgot to state that I can't change reverse method, as many have suggested so far...
if you can change the method you wanna call to
public static ActionDefinition reverse(String action, Map<String, ? extends Object> args)
Change the method signature of reverse to use generics
public static ActionDefinition reverse(String action, Map<String, ? extends Object> args)
Cast it to simple (Map), but beware you are cheating.
You can always cast it to Map because it is one, and you can always feed a raw type into a method because of backwards compatibility, so casting a parametrized type to a raw one is always a way to convert it to any other parameters. But you should only do that when you know that it won't introduce a bug and if you have no sensible alternative.

Categories