I have the following code:
public class A implements B{
private final Map<String, Map<String, Object>> processors = new ConcurrentHashMap<>();
...
#override(from B)
public void doSomething() {
processors.putIfAbsent(new ConcurentHashMap<>()); ->compilation error
}
The following error is raise:
The method putIfAbsent() is undefined for the type Map<String,Map<String,object>
Can somebody explain to me which is the cause of this error?
You are missing your key when you call putIfAbsent. You are only passing the value (i.e. the map) as an argument.
Also: What is a? Try something like
processors.putIfAbsent("a key", new ConcurrentHashMap<String, Object>());
Related
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.
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
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.
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.