In the below class I am declaring myMap
public class AllMap {
public static final Map<String, String> myMap= new HashMap<>();
static {
Map.put("yy", "AA");
Map.put("xx", "BB");
}
}
I need to access map in other class.
public class Test {
FieldMap.Map;
}
Everything is working fine,but sonar is giving warning on 1st class:
Make this member "protected".
on the line
public static final Map<String, String> myMap = new HashMap<>();
Should I ignore this warning or should I change it to protected?
If you need to access the map in other classes, then you should protect it against modifications:
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class AllMap {
public static final Map<String, String> myMap;
static {
final Map<String, String> tmpMap = new HashMap<>();
tmpMap.put("yy", "AA");
tmpMap.put("xx", "BB");
myMap = Collections.unmodifiableMap(tmpMap);
}
}
You can still use it and SonarQube won't mark this as error (because the map is read-only).
Read more about Collections#unmodifiableMap(Map).
Sonar is giving you suggestion that your "member" which is:
public final Map<String, String> myMap = new HashMap<>();
should not be public.
Why?
Leaving this as public makes it available from any other package - so you are exposing the member to everybody. Below code is accessing the member directly:
AllMap allMap = new AllMap();
allMap.myMap.put("X", "Y");
In most cases members should be private and accessed by getters and setters, which could prevent with returning the same reference - so you can implement some logic before you get the reference or set it.
If you need to make it static, make static getters and setters.
Sonar lint issue because you are exposing references to mutable objects to client code.Here you are exposing a Map though it is final the final object allows clients to modify the contents of the object.
Never initialize such a field to a client-provided object reference or return the object reference from an accessor.
private static final SomeType [] THE_THINGS = { ... };
public static final List<SomeType> SOMETHINGS =
Collections.unmodifiableList(Arrays.asList(THE_THINGS));
Reference link
Reference link2
Related
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.
Currently have code that initializes a Function<String, Object> lookup variable:
Map<String, String> map = new HashMap<>();
...
lookup = map::get;
Is it possible to derive the HashMap from this lookup variable? According to (https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html) there may not be. Possibly a domain getter and an output getter for Function<String, Object> types?
This might or might not feasible: when Java generate the lambda, it creates an implementation of Function which reference variables used by the lambda as synthetic fields. Since the lambda is map::get, there is a synthetic field for the map.
Using this code and jdoodle, or any java 11 compiler:
import java.util.*;
import java.util.function.*;
import java.lang.reflect.*;
public class MyClass {
public static void main(String args[]) {
Map<String, String> map = new HashMap<>();
Function<String, String> lookup = map::get;
System.out.println(lookup);
System.out.println(lookup.getClass());
for (Field field : lookup.getClass().getDeclaredFields()) {
System.out.println(field);
}
}
}
If you execute the code, you should see a field:
MyClass$$Lambda$1/0x0000000100060c40#4c3e4790
class MyClass$$Lambda$1/0x0000000100060c40
private final java.util.Map MyClass$$Lambda$1/0x0000000100060c40.arg$1
The MyClass$$Lambda$1/0x0000000100060c40.arg$1 is the reference to map.
As you can see, the name is generated and not very easy to predict: you could certainly assume "somewhere" that if there is one field of type Map, then it may be the map used in map::get.
The field is private and with Java 11, you don't know the module of the generated class: you may not even access it using reflection (or java.lang.invoke.MethodHandle).
The short answer is no, it is not reasonably possible. Neither I think you should try to do it unless you want to know how it works.
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.
I am trying to initialize a Map to zeros in a class. I am doing that in this way:
public class A{
private final Map<String,Integer> myMap;
public A(){
this.myMap = new HashMap<String,Integer>() {
{
put("a",0);
put("b",0);
}
};
}
}
My question: Is this a good implementation? Is there anything wrong with this? Or is there any better way to implement this?
What Rod_Algonquin meant was:
public class A {
private final Map<String,Integer> myMap;
public A() {
this.myMap = new HashMap<String,Integer>();
this.myMap.put("a",0);
this.myMap.put("b",0);
}
}
Following up on Luiggi Mendoza's comment, since the Map is declared final, you might have meant for the map to be unmodifiable, but final does not assure that. This will:
public class A {
private final Map<String,Integer> myMap;
public A() {
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("a",0);
map.put("b",0);
this.myMap = Collections.unmodifiableMap(map);
}
}
A better way would be to simply put the values in after you've initialized the map:
myMap = new HashMap<>();
myMap.put("a",0);
myMap.put("b",0);
What your current version is doing is that it's using an instance initializer block (a.k.a. double brace initialization), which creates an unnecessary anonymous class in the background. There is no real benefit in doing so here. On the contrary, it will likely cause a small performance hit.
If your real goal is the creation of an immutable Map, I highly recommend using Guava's ImmutableMap.Builder
The following code doesn't compile
import com.google.common.collect.LinkedHashMultimap;
public class Test {
public static void main(String[] args) {
LinkedHashMultimap<String, String> p = new LinkedHashMultimap<String, String>();
}
}
the error is: The constructor LinkedHashMultimap() is not visible
I have imported the google Java libraries, they are in the build path
and
LinkedHashMultimap<String, String> p;
alone doesn't cause compile error... weird
Use the static create method to get a new instance of LinkedHashMultimap.
LinkedHashMultimap<String, String> p = LinkedHashMultimap.create();
LinkedHashMultimap has no public constructors and the way to get an instance is by using the factory method create. The reason for this is the loophole in Java Generics due to which type inference works only for methods and not for constructors. This means that instead of for example
LinkedHashMultimap<String, Map<String, List<String>>> =
new LinkedHashMultimap<String, Map<String, List<String>>>();
you can write
LinkedHashMultimap<String, Map<String, List<String>>> =
LinkedHashMultimap.create();
With Java 7 the pressure has subsided due to the "diamond operator".