I have a private instance
private final Map<Class<?>, ?> map;
Syntactically, this is correct. What I want to do is this.
public class User {
}
public class UserSubclass extends User {
}
public class Role {
}
map.put(User.class, new User()); // valid
map.put(User.class, new UserSubclass()); // valid
map.put(Role.class, new Role()); // valid
// But when I do the following I need to get an error
map.put(User.class, new Role(); // invalid, compiler error
How should I declare the Map?
How can I instantiate an object of HashMap to this Map?
No, a simple java.util.Map does not support this. It is statically typed, and what you ask for is basically dynamic typing of one parameter based on the runtime-type of another one.
However, the Guava class ClassToInstanceMap implements exactly what you want:
A map, each entry of which maps a Java raw type to an instance of that type. In addition to implementing Map, the additional type-safe operations putInstance(java.lang.Class<T>, T) and getInstance(java.lang.Class<T>) are available.
You cannot do this by default, but what you can do, is to create your own Generic Safe Map, which will work.
The GenericMap would look like this:
class GenericMap extends HashMap<Class<?>, Object> {
public <T> T putClassAndInstance(Class<T> c, T o){
return c.cast(super.put(c, o));
}
public <T> T getInstance(Class<T> c) {
return c.cast(super.get(c));
}
}
And can then be used like:
GenericMap map = new GenericMap();
map.putClassAndInstance(User.class, new User()); // valid
map.putClassAndInstance(User.class, new UserSubclass()); // valid
map.putClassAndInstance(Role.class, new Role()); // valid
map.putClassAndInstance(User.class, new Role()); // invalid, compiler error
This way, you don't have to create special methods for the User and Role, and still have the safety of not adding the wrong object for the wrong type.
Related
Is there any way to set the return value of a method to Encoder<Cat> when Cat.class was passed as an argument?
I'm exploring reworking an API to be as generic as possible. I have a series of interfaces for data encoders but this api is for multiple applications. I want the application author to be able to define the "available encoders" so that their API reflects what's available accurately.
For example, if app developer Bob wants to offer an encoder for the type Cat he might do:
encoderRegistry.register(Cat.class, new CatEncoder());
Then, someone using his apps' API could get the encoder for a specific type and would see the appropriate type-based arguments:
encoderRegistry.get(Cat.class).encode(Cat cat);
I'm trying to avoid Object types because I want the API to be as user-friendly as possible and if the IDE pops up a method accepting Cat, there's no question.
So far the only way I've been able to get close is by adding a type to the get method:
encoderRegistry.<IEncoder<Cat>>get(Cat.class)
This properly informs the return value for get so that I can use encode(Cat cat) but it's verbose, and there's no stopping me from using encoderRegistry.<IEncoder<Cat>>get(Dog.class)
You can set up the API to accept the right types, but internally you will need to type cast. Java's type system is not strong enough to represent heterogenous type maps, so you will need to depend on the compiler to enforce safety at the API edges. Example:
interface Encoder<T> {
String encode(T value);
}
static class EncoderRegistry {
Map<Class, Encoder> encoders = new HashMap<>();
<T> void register(Class<T> clz, Encoder<? super T> encoder) {
encoders.put(clz, encoder);
};
<T> Encoder<T> get(Class<T> clz) {
return (Encoder<T>) (Encoder) encoders.get(clz);
}
}
public static void run() {
class Cat {}
class CatEncoder implements Encoder<Cat> {
#Override
public String encode(Cat value) {
return value.toString();
}
}
var registry = new EncoderRegistry();
registry.register(Cat.class, new CatEncoder());
registry.get(Cat.class).encode(new Cat());
}
I have the following classes
public class MyCustomFactory extends SomeOther3rdPartyFactory {
// Return our custom behaviour for the 'string' type
#Override
public StringType stringType() {
return new MyCustomStringType();
}
// Return our custom behaviour for the 'int' type
#Override
public IntType intType() {
return new MyCustomIntType();
}
// same for boolean, array, object etc
}
Now, for example, the custom type classes:
public class MyCustomStringType extends StringType {
#Override
public void enrichWithProperty(final SomePropertyObject prop) {
super.enrichWithProperty(prop);
if (prop.getSomeAttribute("attribute01")) {
this.doSomething();
this.doSomethingElse();
}
if (prop.getSomeAttribute("attribute02")) {
this.doSomethingYetAgain();
}
// other properties and actions
}
}
But each custom type class like the string one above might have exactly the same if (prop.getSomeAttribute("blah")) { // same thing; }
Suppose I was to add another attribute, is there a nice way I can avoid having to duplicate if statements in each custom type class that needs it? I can move each if statement to utility class but I still need to add the call to the method in the utility class. I think we can do better.
You can create Map<String, Consumer<MyCustomStringType>>, where the key is your attribute name and value is the method call.
public class MyCustomStringType extends StringType {
private final Map<String, Cosnumer<MyCustomStringType>> map = new HashMap<>();
{
map.put("attribute01", o -> {o.doSomething(); o.doSomethingElse();});
map.put("attribute02", MyCustomStringType::doSomethingYetAgain);
// other properties and actions
}
#Override
public void enrichWithProperty(final SomePropertyObject prop) {
super.enrichWithProperty(prop);
map.entrySet().stream()
.filter(entry -> prop.getSomeAttribute(entry.getKey()))
.forEach(entry -> entry.getValue().accept(MyCustomStringType.this));
}
}
Depending on how you initialise this class (and whether this map is always the same), you might be able to turn in into static final immutable map.
I would also recommend naming it better, but a lot here depends on your domain and what this map and loop actually do.
I have an interface called InterA which has a method call operate() that has its return value as Result.
public interface InterA<T>
{
Result operate(T source);
}
Assume Class MachineOperator and Class CarOperator are implementations of the above with two T object types.
public class MachineOperator implements InterA<Machine>
{
Result operate(Machine source)
{
//Do some operation here and return result.
}
}
public class CarOperator implements InterA<Car>
{
Result operate(Car source)
{
//Do some operation here and return result.
}
}
And I want to keep these implementations in a map so that when I give the class I can have the implementation. So my map would be like this.
Map<Class<?>, InterA<T>> map = new HashMap<>();
And when I go to fill the map I have to do following,
map.put(Machine.class, (InterA<T>) new MachineOperator());
map.put(Car.class, (InterA<T>) new CarOperator());
Is there any way of defining the map so there is no longer a need to cast when adding?
=============================================================
I have tried the following also which can avoid casting when adding to map.
public interface InterA
{
Result <T> operate(T source);
}
Then in each implementation I have to have additional casting for each T.
public class MachineOperator implements InterA
{
< Machine> Result operate(Machine source)
{
(Machine) source; //Need to cast for Machine In-order to read attributes.
//Do some operation here and return result.
}
}
Out of these what would be the correct approach? Is there any different solution?
You could write a wrapper for your Map to allow adding only corresponding InterA objects:
class ClassMap {
private Map<Class<?>, InterA<?>> map = new HashMap<>();
public <T> void put(Class<T> key, InterA<T> value) {
map.put(key, value);
}
//we suppress unchecked cast warning because for Class<T> key we can put only InterA<T> value
#SuppressWarnings("unchecked")
public <T> InterA<T> get(Class<T> key) {
return (InterA<T>) map.get(key);
}
}
then you can use it like this
ClassMap map = new ClassMap();
map.put(Machine.class, new MachineOperator());
InterA<Machine> operator = map.get(Machine.class);
You should replace T in Map declaration with some common ancestor for Car and Machine.
If there is no common class, just use Object or ?.
That will eliminate compilation errors, but will increase chances of runtime errors. You will have be sure that the instance of the parameter is what is expected.
The Lower Bounded Wildcards is what you are looking for.
Your map declaration should looks like that:
Map<Class<?>, ? super InterA<?>> map = new HashMap<>();
I want to create a Map<Long, Enum< ? extends SomeInterface>. Which is the best option for me?
I tried this one
private Map<Long, Enum<? extends SomeInterface>[]> questionIdToanswersMapping = Collections.unmodifiableMap(Stream.of(
new SimpleEntry<>(QuestionEnum1.getQuestionId(), AnswerEnum1.values()),
new SimpleEntry<>(QuestionEnum2.getQuestionId(), AnswerEnum2.values()),
new SimpleEntry<>(QuestionEnum3.getQuestionId(), AnswerEnum3.values()),
new SimpleEntry<>(QuestionEnum4.getQuestionId(), AnswerEnum4.values()),
new SimpleEntry<>(QuestionEnum5.getQuestionId(), AnswerEnum5.values()))
.collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue())));
But it is giving error "cannot convert from Map<Object,Object> to Map<Long,Enum<? extends SomeEnum>[]>". I am new to this. Please help!
I need unmodifiable map of question Id to the corrosponding possible answers values. Possible answers are Enums
Possible Answers are wrapped like this :
public class RecognizedAnswers {
public enum AnswerEnum1 implements SomeInterface;
public enum Answer2 implements SomeInterface;
}
There is a small problem with naming I think:
You cannot extend one enum with another in java, use interface with desired method instead, like below
And below code is working fine:
#Test
public void test() {
Map<Long, Enum<? extends SomeEnum>[]> questionIdToanswersMapping = Collections.unmodifiableMap(Stream.of(
new AbstractMap.SimpleEntry<>(QuestionEnum1.A.getQuestionId(), AnswerEnum1.values()),
new AbstractMap.SimpleEntry<>(QuestionEnum1.B.getQuestionId(), AnswerEnum1.values()),
new AbstractMap.SimpleEntry<>(QuestionEnum1.C.getQuestionId(), AnswerEnum2.values()),
new AbstractMap.SimpleEntry<>(QuestionEnum1.D.getQuestionId(), AnswerEnum2.values())
)
.collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue())));
System.out.print(questionIdToanswersMapping.size());
}
enum QuestionEnum1 {
A, B, C, D;
Long getQuestionId() {
return (long) name().hashCode(); // my mocked values
}
}
interface SomeEnum {
}
enum AnswerEnum1 implements SomeEnum {
}
enum AnswerEnum2 implements SomeEnum {
}
I tried to replicate your example (since you obfuscated the enum types, I made up my own) and it appears to compile just fine:
enum SomeEnum { FOO, BAR }
private Map<Long, Enum<? extends SomeEnum>[]> exampleMap =
Collections.unmodifiableMap(Stream.of(
new SimpleEntry<>(1L, SomeEnum.values()))
.collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue)));
My guess is that you have either a missing parenthesis, or your QuestionEnum1.getQuestionId() returns an int rather than a long, and those things are confusing the compiler enough that it can't give a clear error message.
I'll note that the Stream API really isn't a clean way to construct a constant map. Simply building such a map "normally" with Map.put() will likely be simpler and easier to read, even if it requires a static {} block or a helper function. You can do even better with Guava's immutable collections, which could be used like so:
private final ImmutableMap<Long, Enum<? extends SomeEnum>[]> questionIdToanswersMapping =
ImmutableMap.builder()
.put(QuestionEnum1.getQuestionId(), AnswerEnum1.values())
.put(QuestionEnum2.getQuestionId(), AnswerEnum2.values())
.put(QuestionEnum3.getQuestionId(), AnswerEnum3.values())
.put(QuestionEnum4.getQuestionId(), AnswerEnum4.values())
.put(QuestionEnum5.getQuestionId(), AnswerEnum5.values())
.build();
Much clearer and easier to read (and write).
I'd like to create a map of singleton classes that I can access via a cross reference in order to respond to a specific request. I have the following implemented, but having trouble getting to an actual reference that I can call getInstance() on.
Map<Integer, Class<? extends Thing>> xref = new HashMap<Integer, Class<? extends Thing>>();
xref.put(1, ThingOne.class);
xref.put(2, ThingTwo.class);
Class<? extends Thing> t = xref.get(1);
Ultimately then do something like...
something.perform(arg1, arg2);
Can't figure out how to get from "t" to "something", or if that's possible given the way I have it coded. I tried calling .cast(Thing.class).getInstance(), but got a Cast exception. Also tried reflection to get the getInstance() method, but no luck there either.
It may be I'm going down the wrong path altogether. Given 1..n possible functions, any given instance of the solution may only require a subset of these. In addition, I'd like to easily add/delete classes and manage the interface through config vs. a bunch of object instantiations at startup time.
Thanks!!!
I don't quite understand your purpose in creating this map. From what you've written, it seems you could simply put static getInstance() methods, that return singletons, on each relevant class. Or even more trivial: put each shared instance as a static final field of its class.
If you must use a map, don't use an integer as a key. The class is the key, and its instance is the value. Something like:
private static final Map<Class<?>,Object> singletons = new HashMap<>();
public static synchronized <T> T getSingleton(Class<T> klass) {
Object obj = singletons.get(klass);
if (obj == null) {
try {
obj = klass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
singletons.put(klass, obj);
}
return klass.cast(obj);
}
The creation code there is icky and requires a public no-arg constructor. You could alternatively call via reflection a static, specifically named method on each class to create the desired instance ("createInstance"), which might be a bit more flexible, but when you do that, it once again asks why bother with the map, when you could call a method on the class directly?
One interesting possibility with the map is to supply functions to create instances. In Java 8 syntax (import java.util.function.Supplier):
private static final Map<Class<?>,Object> singletons = new HashMap<>();
public static synchronized <T> T getSingleton(Class<T> klass) {
Object obj = singletons.get(klass);
if (obj instanceof Supplier) {
obj = ((Supplier<?>)obj).get();
singletons.put(klass, obj);
}
return klass.cast(obj);
}
public static synchronized <T> void declareSingleton(Class<T> klass, Supplier<T> supplier) {
if (Supplier.class.isAssignableFrom(klass)) {
// prevent Supplier<Supplier<?>> weirdness;
// could use separate maps if those are really needed
throw new UnsupportedOperationException();
}
singletons.put(klass, supplier);
}
static {
// add creation expressions for several classes;
// instances will not be created until needed
declareSingleton(ThingOne.class, () -> new ThingOne());
declareSingleton(ThingTwo.class, () -> new ThingTwo(123));
}
I'm not sure if this is what you want but it might contain some ideas.
Edit: I've just realized a problem of using the Class itself as a key: it causes the class to be loaded even if it is not needed during a particular program run. Using a String key would avoid loading unneeded classes, but increases fragility. This is another argument against using a map for all this.