I have multiple types of objects, I'd like to generalise the 'id' of the objects in a way that will dynamically change what field is selected as the id.
Example
public class ObjectA{
//Attribute name attA
private String attA;
.... More attributes
public String getAttA(){
return attA
}
.....More getters/setters
}
public class ObjectB{
//Attribute named attB
private String attB;
.... More attributes
public String getAttB(){
return attB
}
.... More getters and setters
}
Id like to be able to run something like this:
Map<????, ????> customIdMap = new HashMap<>();
//We decide that ObjectA main attribute is AttA
customIdMap.add(ObjectA.class, ObjectA::getAttA);
//We decide that ObjectB main attribute is AttB
customIdMap.add(ObjectB.class, ObjectB::getAttB);
Then I'll be able to have a list of general objects and ill be able to retrieve their ids from the map if it is a known object with:
public String getCustomId(Object object){
if(customIdMap.contains(object.getClass()){
//Parameters are messed up, but this is the general idea of how
//i thought this would look
return customIdMap.get(object.getClass()).apply(object);
}
}
The code above does not run since getAttA is a call to a none static method in a static context so i assume this maybe should be wrapped in some kind of generic object.
Can it be done?
Preferably you change ObjectA and ObjectB to have a common interface. If that's not possible you can put them into a map like this:
Map<Class<? extends Object>, Function<Object, String>> map = new HashMap<>();
map.put(ObjectA.class, a -> ((ObjectA) a).getAttA());
map.put(ObjectB.class, b -> ((ObjectB) b).getAttB());
EDIT:
Or if you would like to encapsulate it into a typesafe heterogeneous container:
public static class ToIdMap {
private final Map<Class<?>, Function<Object, String>> map = new HashMap<>();
public <X> void put(Class<X> clazz, Function<X, String> func) {
map.put(clazz, (Function<Object, String>) func);
}
public String toIdString(Object o) {
return map.get(o.getClass()).apply(o);
}
}
EDIT2: Note that neither of these solutions work for subclasses, but it could be supported by traversing the class hierarchy in toIdString.
Your wording is a bit unclear, but I assume you want to get the ID of an object, even when they are different classes. This is the problem that interfaces solve.
You can create an interface, with one method called getId(), which will return the id. Then, you can just call getId() on any type of object with an id.
For example:
public interface Identifiable {
String getId();
}
public class ObjectA implements Identifiable {
// same for ObjectB
#Override
public String getId() {
return id;
}
}
Then, in your code:
Identifiable i1 = new ObjectA();
Identifiable i2 = new ObjectB();
System.out.println(i1.getId());
System.out.println(i2.getId());
EDIT:
It still looks like an interface is the cleanest way of solving your problem. For completeness, the following will work:
Map<Class, Function<?, String> map = new HashMap<>();
map.put(Object1.class, (Object1 o) -> o.getAttrA); // repeat for ObjectB
It can then be called with:
if (obj instanceof Object1) return map.get(Object1.class).apply((ObjectA) obj);
Ended up doing this weird solution:
class Mapping<T> {
private Function<T, String> idFunc;
public Mapping(Function<T, String> idFunc) {
this.idFunc = idFunc;
}
public String apply(T obj) {
return idFunc.apply(obj);
}
}
}
private Map<Class, Mapping> mappings = new HashMap<>();
mappings.put(ObjectA.class, new Mapping<>(ObjectA::getAttA);
mappings.put(ObjectB.class, new Mapping<>(ObjectB::getAttB);
public String getObjectID(Object object){
String id = null;
if(mappings.containsKey(object.getClass())){
id = mappings.get(object.getClass()).apply(object);
}
return id;
}
Related
This is an odd question. I don't think there's a solution, but I thought I'd ask anyway.
Say I have an enum:
public enum Key {
RED(String.class),
GREEN(Integer.class),
BLUE(Short.class);
private Class<?> expectedType;
Key(Class<?> expectedType) { this.expectedType = expectedType; }
public Class<?> getExpectedType() { return expectedType; }
}
I want to use the 'expectedType' field from the Key enum as the return type of a method. See:
public class Cache {
private static Map<Key, Object> cache = new HashMap<>();
public void put(Key key, Object value) {
// Easy to validate that 'value' is of type key.getExpectedType()...
}
public <T> T get(Key key) {
Object value = cache.get(key);
// TODO need to define <T> as key.getExpectedType(). How?
}
}
See that TODO? I'd like for get() to define the return type of the 'expectedType' defined by the key parameter. E.g. if the key parameter were RED, the get() method would return a String and you could write:
String s = cache.get(Key.RED);
Is there a way to do that?
I'm thinking there isn't, but I'd love to hear of a clever solution.
Enums don't support generics, but you could use a regular class as a generic pseudo-enum:
public class Key<T> {
public static final Key<String> RED = new Key<>(String.class);
public static final Key<Integer> GREEN = new Key<>(Integer.class);
public static final Key<Short> BLUE = new Key<>(Short.class);
private final Class<T> expectedType;
private Key(Class<T> expectedType) { this.expectedType = expectedType; }
public Class<T> getExpectedType() { return expectedType; }
}
public class Cache {
private Map<Key<?>, Object> cache = new HashMap<>();
public <T> void put(Key<T> key, T value) {
cache.put(key, key.getExpectedType().cast(value));
}
public <T> T get(Key<T> key) {
return key.getExpectedType().cast(cache.get(key));
}
}
shmosel's answer is almost certainly sufficient for what you need; however, it has the slight limitation that you can't store/retrieve a generic type, because you can't get a class literal for a generic type.
Instead, you can use something like Guava's TypeCapture:
abstract class GenericKey<T> {
Type getExpectedType() {
return ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
which is a bit of reflective grossness that you shouldn't spend too much time looking at.
Notice that it's abstract, so you have to instantiate like:
new GenericKey<Integer>() {}
This is creating an anonymous subclass of GenericKey, which is part of the magic that makes it work with generic types.
Then, it's basically the same:
public class Cache {
private Map<GenericKey<?>, Object> cache = new HashMap<>();
public <T> void put(GenericKey<T> key, T value) {
cache.put(key.getExpectedType(), value);
}
public <T> T get(GenericKey<T> key) {
return (T) cache.get(key.getExpectedType());
}
}
Now you could have a GenericKey<List<Integer>>, using new new GenericKey<List<Integer>() {}, if you should so desire.
The downside of this approach is that you lose the ability to do checking on the value on the way in/out of the cache, so you could get heap pollution if you are careless with raw types.
I have some code below representing a parent and child Pojo, and a simple validator that pulls two values off them representing ranges, to verify that start < end. I want to validator to be generic enough that it can accept two field getter methods at construction time, and then be able to be passed a POJO to perform the range check on. However, I have been unable to get this to type check properly. I have tried having the validator constructor taking all of the following:
Function<Pojo, Integer> //Fails on constructing vlad2 - "Incompatible types in lambda expression: Expected Pojo but found ExtendedPojo".
Function<? extends Pojo, Integer> //Fails on getRangeStart.apply(pojo) - "(capture<? extends Pojo>) in Function cannot be applied to Pojo"
Function<Object, Integer> //Fails on constructing both vlad and vlad2 - "Incompatible types in lambda expression: Expected Object but found ExtendedPojo"
Code:
import java.util.function.Function;
class Pojo {
private Integer rangeOneStart;
private Integer rangeOneEnd;
public Pojo(Integer rangeOneStart, Integer rangeOneEnd) {
this.rangeOneStart = rangeOneStart;
this.rangeOneEnd = rangeOneEnd;
}
public Integer getRangeOneStart() {
return rangeOneStart;
}
public Integer getRangeOneEnd() {
return rangeOneEnd;
}
}
class ExtendedPojo extends Pojo {
private Integer rangeTwoStart;
private Integer rangeTwoEnd;
public ExtendedPojo(Integer rangeOneStart, Integer rangeOneEnd, Integer rangeTwoStart, Integer rangeTwoEnd) {
super(rangeOneStart, rangeOneEnd);
this.rangeTwoStart = rangeTwoStart;
this.rangeTwoEnd = rangeTwoEnd;
}
public Integer getRangeTwoStart() {
return rangeTwoStart;
}
public Integer getRangeTwoEnd() {
return rangeTwoEnd;
}
}
interface SomeValidatorInterface<T> {
boolean isValid(T obj);
}
class MyValidator implements SomeValidatorInterface<Pojo> {
private Function<Pojo, Integer> getRangeStart;
private Function<Pojo, Integer> getRangeEnd;
MyValidator(Function<Pojo, Integer> getRangeStart, Function<Pojo, Integer> getRangeEnd) {
this.getRangeStart = getRangeStart;
this.getRangeEnd = getRangeEnd;
}
#Override
public boolean isValid(Pojo pojo) {
Integer start = getRangeStart.apply(pojo);
Integer end = getRangeEnd.apply(pojo);
return end > start;
}
}
class Main {
public static void main(String args[]) {
ExtendedPojo pojo = new ExtendedPojo(1,2,3,4);
MyValidator vlad = new MyValidator(Pojo::getRangeOneStart, Pojo::getRangeOneEnd);
System.out.println(vlad.isValid(pojo));
MyValidator vlad2 = new MyValidator(ExtendedPojo::getRangeTwoStart, ExtendedPojo::getRangeTwoEnd);
System.out.println(vlad2.isValid(pojo));
}
}
Since the validator is being used per instance just provide a specific instance method as Supplier<Integer>
MyValidator(Supplier<Integer> getRangeStart, Supplier<Integer> getRangeEnd) {
this.getRangeStart = getRangeStart;
this.getRangeEnd = getRangeEnd;
}
// ...
ExtendedPojo pojo = new ExtendedPojo(1,2,3,4);
MyValidator vlad = new MyValidator(pojo::getRangeOneStart, pojo::getRangeOneEnd);
If you don't want to use such specific construction you need to move the range getter to the common interface or at least the Pojo class and override this in ExtendedPojo
I have either one of following Objects, ObjOne and ObjTwo, going into my function, both sharing similar getters/setters.
Currently I have an intermediary, a mapper, used across internal methods, but there might be a cleaner way to do this without a mapper but lacking specific syntax.
public String mapper(Object obj){
Map<String, String> map = new HashMap<>();
if(obj instanceof ObjOne){
ObjOne obj1 = (ObjOne)obj;
map.put("firstKey", obj1.getFirstValue());
}
else if(obj instanceof ObjTwo){
ObjTwo obj2 = (ObjTwo)obj
map.put("firstKey", obj1.getFirstValue());
}
return secondFunction(map);
}
private String secondFunction(Map<String, String> map){
return thirdFunction(map.get("firstKey"));
}
Is there such syntax for (ObjOne || ObjTwo)obj).getFirstValue() to feed into thirdFunction herein?
Edit: I imported these Objects, so I can't declare a parent class for them, they do share getters/setters that are convenient for my scenario.
A more OO approach is to compose the objects you don't control within a new object that you do control. Then write your API in terms of the object you control.
final class ObjOne {
String getFirstValue() {
return "foo";
}
}
final class ObjTwo {
String getFirstValue() {
return "bar";
}
}
class MyAdapter {
final Map<String, String> map = new HashMap<>();
MyAdapter(ObjOne o1) {
this(o1.getFirstValue());
}
MyAdapter(ObjTwo o2) {
this(o2.getFirstValue());
}
MyAdapter(String firstKey) {
map.put("firstKey", firstKey);
}
}
public String secondFunction(MyAdapter adapter) {
return thirdFunction(adapter.map.get("firstKey"));
}
One suggestion don't pass Object instead do something like this create a base model use here polymorphism. for example.
abstract class BaseObj {
abstract public String getFirstValue();
}
class ObjOne extends BaseObj{
#Override
public String getFirstValue() {
return "something useful";
}
}
class ObjTwo extends BaseObj{
#Override
public String getFirstValue() {
return "something useful";
}
}
not sure what is the use case here but you can always mold accordingly.
public String mapper(BaseObj obj){
Map<String, String> map = new HashMap<>();
map.put("firstKey", obj.getFirstValue()); //common function call
return secondFunction(map);
}
private String secondFunction(Map<String, String> map){
return thirdFunction(map.get("firstKey"));
}
private String thirdFunction(String firstKey) {
return null;
}
So here's a slightly tricky question (for me).
I have a generic object. Call it MyObject. This object has a method which returns something of the type T:
public class MyObject<T>
{
private T _t;
public MyObject(T t)
{
_t = t;
}
//...
public T get()
{
return _t;
}
}
(Obviously my "MyObject" does a bit more but that's the gist).
Now, I want to have a map of this type:
Map<String, MyObject<?>> m = new HashMap<>();
I want to be able to fetch maps using some predefined string name, and these maps can be of any MyObject. For example, I could call:
m.put("map_1", new MyObject<String>("String"));
m.put("map_2", new MyObject<Integer>(new Integer(3));
m.put("map_3", new MyObject<Long>(new Long(5));
etc.
But - and here's the tricky part - I want the map to "remember" the parameterized type of MyObject when I fetch some value from the map. Using
m.get("map_1");
would return a
MyObject<Object>
type, since the map was defined as containing
MyObject<?>
values. Thus:
m.get("map_1").get() // <-- This is an Object, not a String!
What modification (if any) is possible, in order to be able to get the correct - full - information regarding the MyObject fetched object, such that invoking the last line (m.get("map_1")) would return a
MyObject<String>
Thanks :)
Amir.
Typesafe Heterogeneous Containers from Joshua Bloch's Effective Java might work here. Basically you add a Class object to represent the type.
public class MyObject<T>
{
private T _t;
private Class<T> type;
public MyObject( Class<T> type, T t)
{
_t = t;
this.type = type;
}
//...
public T get()
{
return _t;
}
public Class<T> getType() { return type; }
}
Then you could do something like this:
public <T> T get( Map<String, MyObject<?>> map, String key, Class<T> type ) {
return type.cast( m.get( key ).get() );
}
Which is safe and will compile, but will throw a runtime error if you get the type wrong.
(Note I didn't actually compile that, so I might have syntax errors floating around. But most folks don't know how to use Class to cast objects.)
You can get the class.
Class c = m.get("map_1").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
Here is a full test.
public class GenericsTest {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Map<String, MyObject<?>> map = new HashMap<>();
MyObject<String> obj = new MyObject<>("hello");
map.put("greeting", obj);
Class c = map.get("greeting").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
}
static class MyObject<T> {
T t;
public MyObject(T t) {
this.t = t;
}
T get() {
return t;
}
}
}
The type system only knows about types, not objects, and therefore can not distinguish "key1" from "key2", because both are of type String.
If keys have different types, the easiest way is to encapsulate a weakly typed map, and use reflective casts to prove to the compiler the types are correct:
class Favorites {
private Map<Class<?>,?> map = new HashMap<>();
<V> V get(Class<V> clazz) {
return clazz.cast(map.get(clazz));
}
<V> void put(Class<V> clazz, V value) {
map.put(clazz, value);
}
}
Favorites favs = new Favorites();
favs.put(String.class, "hello");
favs.put(Integer.class, 42);
favs.get(String.class).charAt(1);
I was wondering if it was possible to have a Java dictionary of objects where one of the fields of the object is defined to be the key of the dictionary.
To be more specific, here's what I would like: I have defined a class with three fields. One of these fields is an Integer and is unique to each object. I would like this field to be the key of the dictionary.
Yes, of course it's possible.
Example :
Map<Integer,MyClass> map = new HashMap<Integer,MyClass>();
MyClass myObject = new MyClass(...);
map.put (myObject.getIntegerKey(), myObject);
If you want to hide the details:
public interface HasOwnKey<K> {
public K getKey();
}
public class MyMap<K, V extends HasOwnKey<K>> {
{
private Map<K,V> map = new HashMap<>();
public V put(V value) {
{
return this.map.put(value.getKey(),value);
}
public V get(K key) {
return this.map.get(key)
}
... etc
}
public class MyClass extends HasOwnKey<String> {
...
#Override String getKey() { return this.key; }
}
MyMap<String, MyClass> myMap = new MyMap<>();
MyClass obj = new MyClass();
obj.setKey("abc");
myMap.put(obj);
Unfortunately Java 7 doesn't seem to be smart enough to infer K from a declaration like
public class MyMap<V extends HasOwnKey<K>> {
so you have to provide the Key type in two places and cannot do
MyMap<MyClass> myMap = new MyMap<>();
You can do that easily as follows :
public class CustomClass
{
private int primaryKey;
private int secondaryField;
private int tertiaryField;
public CustomClass(int primaryKey, int secondaryField, int tertiaryField)
{
this.primaryKey = primaryKey;
this.secondaryField = secondaryField;
this.tertiaryField = tertiaryField;
}
public int getPrimaryKey(CustomClass object)
{
return object.primaryKey;
}
}
public class Test
{
public static void main(String[] args)
{
CustomClass object = new CustomClass(10, 20, 30);
Map map = new HashMap<Integer,CustomClass>();
map.put(object.getPrimaryKey(object), object);
}
}
You may also want to consider using Enums for doing the same, if the number of such records is fairly less, as they provide more readability.
If you already have created a List of those objects you can use an aggregate operation in java 8 like this:
Map<Integer, List<MyClass>> theMap = list
.stream()
.collect( Collectors.groupingBy(MyClass::myIntegerKey) );