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);
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'm attempting to hold node properties in the following manner (this may be inherently wrong):
public class Property<T> {
protected String key;
protected T value;
public Property(String key, T value) {
this.key = key;
this.value = value;
}
}
public class Node {
protected HashMap<String,Property> properties;
public Node() {
properties = new HashMap<>();
}
However, this has the unfortunate side effect of making my a giant mess of casting. I've been reading everything that might be relevant, but nothing seems to address the essentail problem. Here's my current thought:
public void add(String key, Object value) {
if ( ! (value instanceof Property)) {
value = new Property<>(key, value);
}
properties.put(key, (Property)value);
}
public long get(String key, long x) {
return (long)properties.get(key).value;
}
public long[] get(String key, long[] x) {
return (long[])properties.get(key).value;
}
public String get(String key, String x) {
return (String)properties.get(key).value;
}
// etc
Now this is obviously incrediably stupid, but I'm going around in circles trying to simply be able to grab a node property by key, and be assured of it's type based on the key.
It's that simple. A given key must correspond to a given type, both for adding and for getting.
I honestly feel like I'm misunderstanding something fundamental about the nature of Java.
Try this
// a node representing things of type T
public class Node<T> {
protected HashMap<String,Property<T>> properties;
public Node() {
properties = new HashMap<>();
}
// add a T to the map
public void add(String key, T value) {
properties.put(key, new Property<T>(string, value));
}
}
What concerns me about your example, so far, is that a "Node" looks quite a lot like a hashmap Entry. A better question would be "what are you REALLY trying to do"?
A given key must correspond to a given type, both for adding and for getting.
Assuming you mean for the String key to be an identifier for the element and its type, then you're out of luck, it's simply impossible with generics. One option is to define a custom class with appropriately typed field/getters for each known property.
If you mean for the x parameter, then you can use generics to do something like
public <T> T get(String key, T x) {
return (T) properties.get(key).value;
}
but this sets you up for all sorts of ClassCastException. Your compiler should warn against this. (Note, also, that you won't be able to use primitive types directly.)
As the node class can hold properties of any value type, you need to make an unchecked cast anyways. There's no need to overload the get function, you can cast to whatever the expected return type is:
#SuppressWarnings("unchecked")
public <T> T get(String key) {
return (T) properties.get(key).value;
}
Examples:
Node node = new Node();
node.add("x", 123);
node.add("y", "ABC");
node.add("z", new Date());
int valueX = node.get("x"); // cast to integer and autobox to int
String valueY = node.get("y"); // cast to String
Date valueZ = node.get("z"); // cast to Date
String valueFail = node.get("z"); // this will throw a ClassCastException
It is possible to write an externally typesafe implementation of this, though it'll require some internal casting that the compiler can't prove is correct.
class TypeSafeMap {
public static final class Key<T> {
// deliberately empty; we're knowingly using reference equality
}
private final Map<Key<?>, Object> map;
TypeSafeMap() {
this.map = new HashMap<>();
}
public <T> T get(Key<T> key) {
return (T) map.get(key); // cast is safe, but the compiler can't prove it
}
public <T> void put(Key<T> key, T value) {
map.put(key, value);
}
}
class SomewhereElse {
static final Key<Integer> myIntKey = new Key<Integer>();
static final Key<String> myStringKey = new Key<String>();
public void doWhatever(TypeSafeMap myMap) {
int myInt = myMap.get(myIntKey);
String myString = myMap.get(myStringKey);
}
}
...That said, if you know the entire set of keys in advance, you can (and should) make a custom class with appropriately typed fields, rather than trying to squeeze the whole thing into a map-like structure.
public class Node
{
public static void main (String[] args)
{
Node node = new Node();
node.addProperty("a", 12L);
node.addProperty("b", "i'm a string");
long number = node.getProperty("a");
String string = node.getProperty("b");
}
private Map<String, Object> properties = new HashMap<>();
public void addProperty(String key, Object value){
this.properties.put(key, value);
}
public <T> T getProperty(String key){
return (T) this.properties.get(key);
}
}
The OP is trying to handle a collection of different objects, so generics aren't the way forward. What he's trying to do is have type-safe processing of each specific object within a collection. Here's how you could do that using the visitor pattern.
// Implement this interface in something which needs to process
// an item from the collection in a way specific to the type of that item
interface Visitor {
void visit(Circle c);
void visit(Square s);
}
class Collection {
Map<String, Shape> shapes = new HashMap<>();
void add(String key, Shape shape) {
shapes.put(key, shape);
}
// when you want to process what's behind a key, send in a visitor
void visit(String key, Visitor visitor) {
// ask the shape to be visited by the visitor
shapes.get(key).visit(visitor);
}
}
interface Shape {
void visit(Visitor visitor);
}
class Circle implements Shape {
void visit(Visitor visitor) {
// tells the visitor to treat this object as a circle
visitor.visit(this);
}
}
Let's say you wanted something which draws a particular shape from the collection.
class DrawingVisitor implements Visitor {
void visit(Circle c) {
// use properties only a circle has to draw it
graphics2d.ellipse(c.getRadius(), c.getCenterPoint());
}
void visit(Square s) {
graphics2d.rectangle(s.getTopLeft(), s.getBottomRight());
}
}
Etc
Make sense?
Folks,
Is there any easy way to add generic class in non generic class.
Basically the cache manager will have map of Cache class which is implemented with proper generics.
But in below class we return (getCache method) Cache via get method it requires explicit cast at callers place how to avoid it.
e.g.
public class CacheManager {
private Map<String, Cache<?,?>> cacheMap = new HashMap<String, Cache<?,?>>();
public Cache<?,?> getCache(String cacheName) {
return cacheMap.get(cacheName);
}
public void addCache(String cacheName,Cache<?,?> cache) {
cacheMap.put(cacheName, cache);
}
}
Short answer: No (as far as I know).
The problem here is that what you are doing is not type-safe in Java at all. Have a look at this example:
import java.util.*;
class ClassCast {
public static void main(String[] args) {
HashMap<String, Pair<?, ?>> map = new HashMap<>();
map.put("test", new Pair<String, Integer>("Hello", 5));
Pair<Double, Double> pair = (Pair<Double, Double>) map.get("test");
}
}
class Pair<T,V> {
T a;
V b;
Pair(T a, V b) {
this.a = a;
this.b = b;
}
}
You would expect a ClassCastException here, but it compiles and runs perfectly fine. The reason for this is that the actual class of Pair<String, Integer> and Pair<Double, Double> is in fact just Pair (after type erasure).
To get type safety you have to implement the "Typesafe heterogeneous container pattern" (explained in detail in Effective Java by Josh Bloch). In short, you have to involve the type parameter in the key of your map. Depending on your needs, you might be able to use a class as key directly, otherwise you might have to make a key object.
Example implementation:
public class CacheManager {
private Map<MultiKey, Cache<?,?>> cacheMap = new HashMap<>();
#SuppressWarnings("unchecked")
public <T,V> Cache<T,V> get(String name, Class<T> t, Class<V> v) {
// Type-safe since types are encoded in key(i.e. map will not
// return something with the wrong type), and key is type-checked
// on insertion.
return (Cache<T,V>) cacheMap.get(new MultiKey(name, t, v));
}
public <T,V> void put(String name, Class<T> t, Class<V> v, Cache<T,V> cache) {
cacheMap.put(new MultiKey(name, t, v), cache);
}
class MultiKey {
Object[] keys;
Integer hash = null;
MultiKey(Object... keys) {
this.keys = keys;
}
#Override
public int hashCode() {
if (hash == null) hash = Arrays.hashCode(keys);
return hash;
}
#Override
public boolean equals(Object o) {
if (o == null || !(o instanceof MultiKey)) return false;
return Arrays.equals(keys, ((MultiKey) o).keys);
}
}
}
Example usage:
CacheManager mng = new CacheManager();
mng.addCache("SI", String.class, Integer.class, new Cache<String, Integer>());
Cache<String, Integer> cache = mng.getCache("SI", String.class, Integer.class);
System.out.println(cache);
It's not pretty, but it is actually type-safe. It can be improved depending on the actual situation though, so you should not use this code as is. For example, if you can get the types from the Cache object you don't need the Class arguments in addCache.
I wrote this utility function:
public static <T> List<T> pluck(String fieldName, List list)
throws NoSuchFieldException, IllegalAccessException {
if (list.isEmpty()) {
return new ArrayList<T>();
}
Class c = list.get(0).getClass();
Field f = c.getField(fieldName);
ArrayList<T> result = Lists.newArrayList();
for (Object object : list) {
result.add((T) f.get(object));
}
return result;
}
I copied the idea from underscore.js. The use case is:
ArrayList<Person> people = new ArrayList<Person>;
people.add(new Person("Alice", "Applebee"));
people.add(new Person("Bob", "Bedmington"));
people.add(new Person("Charlie", "Chang"));
List<String> firstNames = pluck("firstName", people);
My problem is that if the caller gets the type wrong, no exception is thrown until the caller tried to get an object from the list. Ideally, I'd like to throw a ClassCastException from the pluck method itself. However, I don't see a way to access the type of the list on run time.
Is there some trick I can use to make sure the caller doesn't end up with an invalid list?
Edit: So using the feedback I got, a safe implementation would be:
public static <T,F> List<F> pluck(String fieldName, Class<F> fieldType,
List<T> list, Class<T> listType)
throws NoSuchFieldException, IllegalAccessException {
Field f = listType.getField(fieldName);
ArrayList<F> result = new ArrayList<F>();
for (T element : list) {
result.add(fieldType.cast(f.get(element)));
}
return result;
}
But actually lambdaj seems to do what I wanted, so I guess I'll use that. Thanks mike!
Disclaimer: LambdaJ ( #GoogleCode | #GitHub ) - This project is not maintained anymore since the release of JDK8 (JSR 335, JEP 126).
You can change your signature to as follows:
public static <T, F> List<F> pluck(String fieldName, Class<F> fieldType,
List<T> list, Class<T> listType)
The you have the list type and field type.
Why dont you define the signature like this:
public static <T, U> List<T> pluck(String fieldName, Class<T> fieldType, List<U> list);
This would:
1) Force the client to supply the type of the field he wants to "pluck", so you can do proper type-checking in your method.
2) Force the client to supply a generic list from which to "pluck", so you prevent another error-source (the client supplying a list that contains objects of different types).
I think this is as safe as it can get..
What do you mean by invalid list? If you mean that they try to cast it to something it is not then try changing the declaration to public static <T> List<T> pluck(String fieldName, List<T> list).
I'm confused by the However, I don't see a way to access the type of the list on run time. comment. However, if I understand you correctly then: there is no "type" at runtime because generics in Java are implemented by "erasure". This means that the compiler checks at compile time that it works, and then turns it into regular casts like we had before generics. This was necessary they felt to enable backward and forward compatibility.
You should use generics for the type parameter, and pass in the class object of the return type:
public static <TItem, TResult> List<TResult> pluck(String fieldName, List<TItem> list, Class<TResult> resultType)
throws NoSuchFieldException, IllegalAccessException {
if(list.isEmpty()) return new ArrayList<TResult>();
Class c = list.get(0).getClass();
Field f = c.getField(fieldName);
ArrayList<TResult> result = new ArrayList<TResult>();
for(Object object : list) {
result.add(resultType.cast(f.get(object)));
}
return result;
}
Generally, when you get a warning about an unsafe cast to a type parameter, you should see if you can replace it with a call to Class.cast
you can try the one given by Google collections library instead of maintaining a new one: Collections2.transform like this
Collection<Y> yourCollection...
...
Collection<X> expected = Collections2.transform(yourCollection, new Function<Y, X>() {
public X apply(Y element) {
return element.getX();
}
}
With Google's Guava collections library, you can use Collections2.transform().
Usage
Given an interface/class, e.g. called Entity, your class can implement/extend this.
public abstract class Entity {
private long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
public interface Entity {
long getId();
}
Now you can Retrieve a list of each Entity's IDs.
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
public class Main {
public static void main(String[] args) {
List<Entity> entities = ...
List<Long> ids = pluckIds(entities);
}
public static <E extends Entity> List<Long> pluckIds(List<E> list) {
return new ArrayList<Long>(Collections2.transform(list, new Function<E, Long>() {
public Long apply(E entity) {
return entity.getId();
}
});
}
}
This is the safest you can get. This satisfies proper OOP principles and Java 5-7.
In Java 8, you can achieve the same effect with a stream, map, and lambda
public static <E extends Entity> List<Long> pluckIds(List<E> list) {
return list.stream().map(e -> e.getId()).collect(Collectors.toList());
}
or
public static <T,F> List<F> pluck(String fieldName, Class<F> fieldType,
List<T> list, Class<T> listType) throws NoSuchFieldException,
IllegalAccessException, IllegalArgumentException {
Field f = listType.getDeclaredField(fieldName);
f.setAccessible(true);
return list.stream().map(e -> {
try { return fieldType.cast(f.get(e)); } catch (Exception e1) { return null; }
}).collect(Collectors.toList());
}
Not sure what you are asking, but you could try:
Class c = list.get(0).getClass();
if (!c.equals(Person.class))
throw new ClassCastException();
You can cast the list to a java.lang.reflect.ParameterizedType and checking that the array returned by getActualTypeArguments() contains the class you need. Other than that, you're out of luck.
I know this sounds a little crazy but here it is. I have an enum type that represents represents a bunch of different properties. Each could be just a string but it would be nice to enforce some kind of type safety. So basically check the type associated with each enum value and throw an exception if there is a mismatch. I guess it could be done with instance of but I am curious if there is another way to do this without instanceof. I know that may not be possible but I am curious.
Edit, I created a new example that I think illustrates what I am asking better:
public class CmisProperties {
public enum CmisPropEnum{
Name (PropertyIds.NAME, new String() ),
CreatedBy (PropertyIds.CREATED_BY, new String() ),
CreationDate (PropertyIds.CREATION_DATE, new Date() ),
LastModifiedBy (PropertyIds.LAST_MODIFIED_BY, new String() ),
LastModificationDate (PropertyIds.LAST_MODIFICATION_DATE, new Date() ),
ChangeToken (PropertyIds.CHANGE_TOKEN, new String() );
private String propId;
CmisPropEnum ( String propId , Object templateObject ){
this.propId = propId;
}
public <T> String getPropId(){
return propId;
}
}
private Map<CmisPropEnum, Object> propertyMap = new HashMap<CmisPropEnum, Object>();
public Object getProperty(CmisPropEnum propEnum){
return propertyMap.get(propEnum.getPropId());
}
public void setProperty( CmisPropEnum propEnum, Object value){
propertyMap.put(propEnum, value);
}
}
Later on I want this to happen:
CmisProperties props = new CmisProperties();
/* This causes a compile time exception */
props.setProperty(CmisPropEnum.CreationDate, "foobar" );
/* This I want to be ok, because the type matches that in the enum */
props.setProperty(CmisPropEnum.CreationDate, new Date() );
Check out Josh Bloch's Effective Java, Item 29, where he describes a "typesafe heterogeneous container" that he calls Favorites. The API is
public class Favorites {
public <T> void putFavorite(Class<T> type, T instance);
public <T> T getFavorite(Class<T> type);
}
I think it would fit your needs (probably change the name???). You could call
favorite.putFavorite(Name.getClass(), "Fred");
favorite.putFavorite(ADate.getClass(), new Date(1234));
and later
Date date = favorite.getFavorite(ADate.getClass());
As already mentioned by irreputable, you need classes to have variability based on types (i.e. generics). This is a corresponding generic version of your example:
public class Properties {
public static class Property<E> {
private Property(String name) { this.name = name; }
private final String name;
public String getName() { return name; }
}
public static final Property<String> NAME = new Property<String>("name");
// ... other properties
private Map<Property<?>, Object> propertyMap =
new HashMap<Property<?>, Object>();
#SuppressWarnings("unchecked")
public <E> E getProperty(Property<E> property){
return (E) propertyMap.get(property);
}
public <E> void setProperty(Property<E> property, E value){
propertyMap.put(property, value);
}
}
The usage is type-safe and checked at compile-time:
Properties p = new Properties();
p.setProperty(Properties.NAME, "a string"); // only strings allowed for NAME
String s = p.getProperty(Properties.NAME); // can only get strings for NAME
Enums can't be generic, so we need a normal class
public class Prop<T>
{
// some predefined props
static public final Prop<String> NAME = new Prop<>("Name", String.class);
...
public Prop(String name, Class<T> type) // it's ok, anyone can create new kind of Prop
{...}
Class<T> getClassT() {...}
}
then set/get property methods can have stronger static type checking:
private Map< Prop,Object > propMap = new HashMap<>();
public <T> void setProperty(Prop<T> key, T value){
propMap.put(key, value);
}
#SuppressWarnings("unchecked")
public <T> T getProperty(Prop<T> key)
{
return (T)propMap.get(key);
}
so that this won't compile
setProperty(Prop.NAME, new Integer(1)); // fail
int x = getProperty(Prop.NAME); //fail
note that, each entry in the propMap has a key Prop<X> and value X for some X, and X can be different from entry to entry. We cannot really express that constraint on Map in Java; but the constraint is indeed enforced by app logic (i.e. setProperty() only inserts such entries)
In getProperty we must suppress unchecked warning. It is justified, since we know the value for the key must be of type T, due to previously mentioned constraint. One trick to avoid explicitly suppressing the warning is by Class.cast()
public <T> T getProperty(Prop<T> key)
{
return key.getClassT().cast( propMap.get(key) );
}
but it's only a trick since essentially we moved #SupressWarnings to Class.cast(). This is a worse version in performance and in semantic clarity.