Normally, I would call this a bug, but this is in the Java Standard Library.
There is a method -- java.awt.Font.getAttributes()
that returns a Map<TextAttribute, ?>
.
Now, this threw me for a loop because I don't know how I can insert elements into this map. After all, the parameterized type for the values we are inserting is ?, so how can we insert?
If it was fetching, I would simply store whatever I get into Object
, since that is a catch all, much like the unbounded wildcard "?" is.
But this is inserting. How can I insert unless I do an unsafe cast?
Found the answer myself -- you cannot.
This is actually a clue that I was misusing the API. In my defense, the API was not very obvious. But long story short, there is a constructor that TAKES IN a Map<TextAttribute, ?>
. This means that we are PROVIDING our own Map
for them to use. Now, Map<TextAttribute, Object>
can definitely fit into Map<TextAttribute, ?>
, so that means that we are good for providing a value to pass into this constructor.
And if the goal is to see what the contents of the Map
are so that we can construct/populate our own, simply extract each object out into an Object
or var
, then do some instanceof
checks to see what it is. From there, you can construct the proper type V
to put for your Map<K, V>
.
Here is a simple code example to more clearly explain the point.
I want to insert a TextAttribute
into a java.awt.Font
.
final Map<TextAttribute, Object> map = new HashMap<>(); //Empty map to hold ALL attributes
map.putAll(someFont.getAttributes()); //These are the pre-existing attributes on my font
map.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON); //Adding an attribute
final Font font = Font.getFont(map); //Create font that has the new attribute + old ones too
EDIT - Special thanks as always to @Holger for showing an even better way to make this work. Here it is below.
final Font font =
someFont
.deriveFont
(
Map.of
(
TextAttribute.STRIKETHROUGH,
TextAttribute.STRIKETHROUGH_ON
)
)
;
If we look at the documentation for this method, Font.deriveFont(Map< extends AttributedCharacterIterator.Attribute,?>)
, we can see that it takes in a Map whose values are of type ?. Furthermore, the documentation itself says that it "Creates a new Font object by replicating the current Font object and applying a new set of font attributes to it." So, it makes a duplicate, and then applies the changes that you passed in as a parameter to it. This is a more efficient way of doing what I described earlier.