I am trying to program a kind of registry for objects of different classes.
I have the following:
public interface DbObject{
void setId(long id);
Long getId();
}
A prototypic class implementing this interface would be the following:
public class BasicDbObject implements DbObject{
private long id=null;
void setId(long id){
this.id=id;
}
Long getId(){
return id;
}
}
I want to build various different Implementations of this Interface.
And I want to be able to have a Map object, that maps from each implementing class to a Map of instances.
Something like this:
Map <Class<C implements DbObject> , Map<Long, C>> registry = new TreeMap/HashMap/SomeOtherKindOfMap (...)
I know I could do something like
Map <String,Map<Long,DbObjects>> registry = new ...
But this way I would have to write some more code for determining names, comparing classes and so on. Is there an easier way to accomplish this?
So what I want to know: is it possible to have class objects as keys in a tree map?
What would be the syntax to declare a map object, that maps from implementing classes C to a map objects each mapping from a long object (the id) to instances of C?
I want to be able to do requests like the following:
BasicObject bo = registry.get(BasicObject.class).get(42);
assuing id did
BasicObject bo=new BasicObject(...);
innerMap = new SomeMap<Long,BasicObject>();
innerMap.put(42,bo);
registry.put(BasicObject.class,innerMap);
before.
Please tell me, if this still is not clear, I have difficulties to explain, since english is not my mother tongue.
Thank you in advance.
Edit:
It turns out, i can do something very close to what I want, when defining a generic class around the map:
public class ObjectRegistry <T extends DbObject>{
private HashMap<Class<T>, TreeMap<Long,T>> registry=null;
ObjectRegistry(){
registry=new HashMap<Class<T>, TreeMap<Long,T>>();
}
public void register(T dbObject){
TreeMap<Long, T> map = registry.get(dbObject.getClass());
if (map==null){
map=new TreeMap<Long,T>();
registry.put((Class<T>) dbObject.getClass(),map);
}
map.put(dbObject.getId(),dbObject);
}
public <T extends DbObject>T get(Class<T> objectClass,long id){
TreeMap<Long, T> map = (TreeMap<Long, T>) registry.get(objectClass);
if (map != null){
return map.get(id);
}
return null;
}
public TreeMap<Long,T> getAll(Class<T> dbObjectClass) {
return registry.get(dbObjectClass);
}
}
I use a TreeMap for the inner mappings since I want to easily return Class instances sorted by id.
So the refined question is:
Is there a way to do this, without the <T extends DbObject> clause in the Class head?
Edit 2:
Thinking through it again, it turns out that John's answer is exactly the solution to this.
Here is my final code:
HashMap<Class<? extends DbObject>, TreeMap<Long, ? extends DbObject>> registry = null;
public <T extends DbObject> T get(Class<T> clazz, long id) {
TreeMap<Long, T> map = (TreeMap<Long, T>) registry.get(clazz);
if (map != null) {
return map.get(id);
}
return null;
}
public <T extends DbObject> void register(T dbObject) {
TreeMap<Long, T> map = (TreeMap<Long, T>) registry.get(dbObject.getClass());
if (map == null) {
map = new TreeMap<Long, T>();
registry.put((Class<T>) dbObject.getClass(), map);
}
map.put(dbObject.getId(), dbObject);
}
public <T extends DbObject> TreeMap<Long, T> getAll(Class<T> dbObjectClass) {
return (TreeMap<Long, T>) registry.get(dbObjectClass);
}
It does not need the <T extends DbObject> clause in the Class head.
So what I want to know: is it possible to have class objects as keys in a tree map?
TreeMap depends on there being a total order over the key space, as established by the key type having a natural order (by implementing Comparable) or by a separate Comparator object that you provide. Classes do not have a natural order. It is conceivable that you could write a suitable Comparator, but that seems very contrived to me.
But why do you specifically need a TreeMap? You didn't describe any requirement that would not be at least as well addressed by any other kind of Map. In particular, I almost always find HashMap to be a better choice, and I don't see any reason why it would be unsuitable in this one. It can certainly have objects of type Class as keys.
Moreover, if indeed you don't need any particular implementation, then you are best off declaring the type simply as a Map. That way you can actually provide any Map implementation, and even change which one you do provide if you ever discover a reason to do so.
What would be the syntax to declare a map object, that maps from implementing classes C to a map objects each mapping from a long object (the id) to instances of C?
You ask that the constraints on the type of each value be dependent on the type of the associated key, but there is no way to declare a type that enforces such a relationship. Whether a particular key or a particular value is appropriate for the Map is a function of the type of the map alone, not of each others' type.
You can write generic methods around access to your map that provide the appearance of what you want, but the data retrieval methods will need to cast. For example:
Map<Class<? extends DbObject>, Map<Long, ? extends DbObject>> registry = /*...*/;
<T extends DbObject> Map<Long, T> getRegistryMap(Class<T> clazz) {
return (Map<Long, T>) registry.get(clazz);
}
<T extends DbObject> T get(Class<T> clazz, Long id) {
Map<Long, T> map = getRegistryMap(clazz);
return (map == null) ? null : map.get(id);
}
<T extends DbObject> T put(Class<T> clazz, Long id, T obj) {
Map<Long, T> map = getRegistryMap(clazz);
if (map == null) {
map = new HashMap<>();
registry.put(clazz, map);
}
return map.put(id, obj);
}
Updated to add:
So the refined question is: Is there a way to do this, without the <T extends DbObject> clause in the Class head?
Yes, what I already wrote. Just slap a plain class declaration around it. You do not need a generic class to have generic methods. In fact, the two are orthogonal. Regular methods of a generic class can use that class's type parameters. That does not make them generic methods. A method is generic if it declares its own type parameter(s), as mine above do. Your get() method also does that, and it is important to understand that the type parameter <T> you declare explicitly in the method signature shadows the class's type parameter of the same name: it is a different T.
Related
I am trying to create a generic converter interface, which will convert T objects to U objects by using the same method name, i.e. convert:
public interface GenericConverter<T, U> {
T convert(U fromObject);
U convert(T fromObject);
}
Of course, generics erasure tranforms both methods into the following during compilation:
convert(object fromObject);
So both methods have the same erasure, which results in an error during compilation.
In my example it is logical that I will always use different object types for T and U. Is there a way to keep the same method name (convert), be able to encapsulate the fact that T and U are different types and ensure that the proper method will be called in each case?
Unless the two types T and U are based in two separate type hierarchies (i.e. each one will always have some distinct superclass), there's no way of having the two methods with same name. It doesn't even make sense semantically in that case - what should be the semantic difference between the two methods if you cannot distinguish the two types in any reasonable matter?
Apart of the suggested renaming of the methods, consider also only having one such method in the interface and instead using a GenericConverter<T, U> and GenericConverter<U, T> wherever you need to transform both ways.
It's not directly possible due to type erasure. Several options have already been listed in the other answers. One of them implicitly aimed at separating the conversions. So instead of having a single converter, you could have two distinct ones:
public interface GenericConverter<T, U> {
U convert(T fromObject);
}
GenericConverter<Integer, String> forward = Converters.integerString();
GenericConverter<String, Integer> backward = Converters.stringInteger();
But note that the GenericConverter interface in this cases is structurally equal to the Function interface - so there is probably no reason to create a new one.
Instead, if you want to have this "forward and backward converter" as some sort of a named entity (with both conversion functions inseparably linked together), you could define an interface for that:
public interface GenericConverter<T, U> {
Function<T, U> forward();
Function<U, T> backward();
}
This could be used as follows:
GenericConverter<Integer, String> converter = Converters.integerString();
String string = converter.forward().apply(someInteger);
Integer integer = converter.backward().apply(someString);
Whether or not this is the "best" solution here depends on the intended usage patterns. One advantage could be that, with a generic (!) utility function like this...
private static GenericConverter<T, U> create(
Function<T, U> forward, Function<U, T> backward) {
return new GenericConverter() {
#Override
public Function<T, U> forward() {
return forward;
}
#Override
public Function<U, T> backward() {
return backward;
}
}
}
creating a new converter would be easy as pie:
public static GenericConverter<Integer, String> integerString() {
return create(
integer -> String.valueOf(integer),
string -> Integer.parseInt(string)
);
}
Problem
When you say,
it is logical that I will always use different object types for T and U
Compiler does not know. Types can be forced to be same, but not to be different (without constraints).
Approach 1
class ConversionSource {}
class ConversionTarget {}
interface GenericConverter<T extends ConversionSource, U extends ConversionTarget> {
T convert(U obj);
U convert(T obj);
}
Now, erasures are different. You get the behavior you want with source you want, but usage is severely restricted because of constraints.
Approach 2
interface InvertibleConverter<T, U> {
U convert(T obj);
InvertibleConverter<U, T> inverse();
}
class Tokenizer implements InvertibleConverter<String, Stream<String>> {
#Override
Stream<String> convert(String obj) {
return Arrays.stream(obj.split(" "));
}
#Override
InvertibleConverter<Stream<String>, String> inverse() {
return new InvertibleConverter<Stream<String>, String>() {
#Override
public String convert(Stream<String> obj) {
return obj.collect(Collectors.joining(" "));
}
#Override
public InvertibleConverter<String, Stream<String>> inverse() {
return Tokenizer.this;
}
};
}
}
Usage can be as follows
InvertibleConverter<String, Stream<String>> splitter = new Tokenizer();
String sentence = "This is a sentence";
Stream<String> words = splitter.convert(sentence);
String sameSentence = splitter.inverse().convert(words);
This approach works even when T and U are identical.
Hope this helps.
Good luck
I want to specify a method in an interface the signature of the method of an implementation (JPA 2 based) is:
List<T> getByStringValue(SingularAttribute<? super T, String> attribute, String value)
I want to specify this method in an interface (Object<? super T, String> is not possible) that abstracts from jpa. The implementing method could have a different signature but i want it to accept SingularAttribute<? super T, String> and i want to use it in type safe query without casting around.
At the end i want to specify all entity interaction in a "Repository" interface and want to give one JPA-based implementation that covers most of it for all entities (to minimize redundant code). I have finished it for CRUD-ops and getAll. Now i wish i could provide a generic approach for getByCriteria (one Critera is enough at the moment).
Java doesn't allow the non-generic-parameter part to itself be some kind of wildcard, eg any class that has certain generic parameters:
<T> void method(*<T, String> o) // can't do this
But you can define an abstract type and have all classes you want to use like this implement it, something like:
interface StringGetter<T> {
T getByStringValue(String value);
}
then:
public class WidgetStringGetter implements StringGetter<Widget> {
public Widget getByStringValue(String value) {
// whatever
}
}
and:
public static <T> T factory(StringGetter<T> getter, String value) {
return getter.getByStringValue(value);
}
finally:
Widget w = factory(new WidgetStringGetter(), "foo");
Try the following.
<R, T extends R> List<T> getByStringValue(SingularAttribute<R, String> attribute, String value)
The <R, T extends R> defines generic parameters for use in the method signature.
I have quite big issue with create generic method for Ordering. At this moment I have this function :
public <T> T orderAscending(Function<?, ? extends Comparable> function, Iterable<? extends LinkedList<?>> sortingList) {
return Ordering.natural().onResultOf(function).sortedCopy(sortingList);
}
First parameter of this function is created in this way :
public static Function<ParkingWebApiDTO, Date> getSortActiveParkingsByStartDate() {
Function<ParkingWebApiDTO, Date> getStartDateFunction = new Function<ParkingWebApiDTO, Date>() {
#Override
public Date apply(ParkingWebApiDTO parkingWebApiDTO) {
return parkingWebApiDTO.getStartDate();
}
};
return getStartDateFunction;
}
and the second one is LinkedList with some custom object in it (List<MyObject> test = new LinkedList<MyObject>()).
Please someone help me to fix this generic method orderAscending. Much appreciated for help.
I guess you meant to create List (sorted by start date) from Iterable of your DTOs (I assume you don't want iterable of lists of DTOs).
So let's say your DTO looks like this:
interface ParkingWebApiDTO { // could be simple class, etc.
Date getStartDate();
// ...and more methods here
}
you have input list:
LinkedList<? extends ParkingWebApiDTO> iterable = Lists.newLinkedList();
and function which retrieves start date from DTO:
Function<ParkingWebApiDTO, Date> function = new Function<ParkingWebApiDTO, Date>() {
#Override
public Date apply(ParkingWebApiDTO dto) {
return dto.getStartDate();
}
};
you expect output like this:
List<? extends ParkingWebApiDTO> result = orderAscending(function, iterable);
which can be achieved with following orderAscending imlementation:
public static <X, T extends Comparable<T>> List<? extends X> orderAscending(
Function<X, T> function, Iterable<? extends X> sortingList) {
return Ordering.natural().onResultOf(function).sortedCopy(sortingList);
}
You need to declare both from and to types as generic types if you want to have "universal" method.
Another thing is if you really need to have such generic name, because using Ordering.natural().onResultOf(function).sortedCopy(list) is perfectly fine and having orderAscending is IMO overkill (you'll end with plenty of methods like this one).
I'm having a very simple yet confuse question at the same time.
In a Transformer, is there a way to specify that the types of the classes being transformed are the same type?
To make it clear, I'll share the code:
Transformer<Set<?>, List<?>> transformer = new SetToListTransformer();
Is there a way for me to specify that the Set and the List are of the same type?
Also when writing the transformer I did this, and I assume it serves no purpose:
private static class SetToListTransformer implements Transformer<Set<?>, List<?>> {
#Override
public List<?> transform(final Set<?> input) {
return this.doTransform(input);
}
public <T> List<T> doTransform(final Set<T> input) {
...
}
}
The thing is, I cannot type the SetToListTransformer since I do not really care about what types are inside, I just care that they are the same type.
Any help would be appreciated!
PS: I'm not really transforming a Set into a List, I'm using other types, I just used them to clarify the code :)
Try to bind both wildcards to the same type paramter, like this:
class SetToListTransformer<E> implements Transformer<Set<E>, List<E>> {
#Override
public List<E> transform(Set<E> from) {
...
}
}
Make your class generic:
private static class SetToListTransformer <T> implements Transformer<Set<T>, List<T>> {
#Override
public List<T> transform(Set<T> input) {
return this.doTransform(input);
}
public List<T> doTransform(Set<T> input) {
...
}
}
Note, however, that this implementation is quite strict with types. You wont be able to use a SetToListTransformer<Number> to convert from Set<Integer> to List<Number>, although Integer IS a Number.
There's no way to enforce the desired constraint on your transform implementation, since there is no way to impose any relationship between generic wildcards. Each of the two ? in your SetToListTransformer declaration are doomed to each mean some unknown type with no way to bound them to each other.
As others pointed out, the easiest solution is to make SetToListTransformer generic. For example:
class SetToListTransformer<T> implements Transformer<Set<? extends T>, List<T>> {
#Override
public List<T> transform(final Set<? extends T> input) {
final List<T> output = new ArrayList<T>(input.size());
output.addAll(input);
return output;
}
}
Of course this requires you to instantiate transformers with specific type arguments. This should be fine as long as SetToListTransformer is cheap. But, you indicated you just want to use one instance. Here's how to do that:
class MyTransformers {
// There is no reason to expose SetToListTransformer now.
// Keep it here as an anonymous class.
private static final Transformer<Set<?>, List<?>> FROM_SET_TO_LIST =
new Transformer<Set<?>, List<?>>() {
#Override
public List<?> transform(final Set<?> input) {
return doTransform(input);
}
private <T> List<T> doTransform(final Set<T> input) {
final List<T> output = new ArrayList<T>(input.size());
output.addAll(input);
return output;
}
};
private MyTransformers() { }
public static <T> Transformer<Set<? extends T>, List<T>> fromSetToList() {
#SuppressWarnings("unchecked")//this is okay for any T because the impl is stateless
final Transformer<Set<? extends T>, List<T>> withNarrowedTypes =
(Transformer<Set<? extends T>, List<T>>)(Transformer<?, ?>)FROM_SET_TO_LIST;
return withNarrowedTypes;
}
}
Here's a usage example:
Set<Integer> intSet = new HashSet<Integer>();
intSet.add(42);
intSet.add(1337);
List<Number> numList = MyTransformers.<Number>fromSetToList().transform(intSet);
You can't express that constraint with the Transformer interface. You may create a subtype that imposes additional constraints, and use the subtype where Transformer was used.
interface StricterTransformer extends Transformer<Set<?>, List<?>>
public <T> List<T> transform2(Set<T> input) ;
/** javadoc additional contract: must behave as transform2(input) */
public List<?> transform(Set<?> input);
// in java8 we can give a "default" impl for this interface method
// that simply calls transform2(), so subclasses don't have to impl
// the transform() method, which is just boiler plate code.
I want to create a kind of in-memory database into which I can put objects of any type that extends EntityProxy. I want to index every object according to its type and id number (which every EntityProxy has - something simple like creating a HashMap for each type.
I could implement this manually with something like
public void put(Object o)
{
if (o instanceof Car)
carIndex.put(o.getId(), o);
else if (o instanceof Bus)
busIndex.put(o.getId(), o);
...
}
But of course I don't want to. I want something more like
public void <T extends EntityProxy> put(T o)
{
indexMap.get(o.class).put(o.getId(), o);
}
I just don't quit have the generics vocabulary to do this. I don't know what the question mark means in template definitions, really - will something like
HashMap<Class<?>, HashMap<Long, EntityProxy>> indexMap
work?
That map is OK; if you really need to add a tiny bit of constraint, just try:
Map<Class<? extends EntityProxy>, HashMap<Long, EntityProxy>> indexMap;
This would make sure the key class can only be an EntityProxy.class or subclass.
You can think of the question mark as some "anything", but anonymous. So <?> means really anything -- any Object, <? extends EntityProxy> means anything that fulfils this condition (passes the "instanceof EntityProxy" test).
The type safety here is less than desired, as you can still put anything as key and anything in that map. I mean, you can legally put this in the map:
indexMap.put(EntityProxy1.class, new HashMap<Long, EntityProxy2>());
(assuming EntityProxy1 and EntityProxy2 are both subclasses of EntityProxy) since there's no correlation between the key and the value. To enforce that, the put() method of the map would need to be declared like this:
<T extends EntityProxy> put(Class<T> key, HashMap<Long, T> value);
T is pretty much like ? but the main difference is that it provides you with a name that you can refer to it in that context.
So, if I said that ? stands for "anything", I would say T stands for "something", as you can refer to that something once you declare it.
But you would need a custom data structure for this, as java.util.Map does not provide this kind of constraint. If you're using it as shown in your code sample, I don't think you really need these enforcements.
Note that in this example I used List. You could easily replace this with your Collection class of choice and adapt accordingly.
public interface Classifiable {
String classification();
}
public abstract class Automobile implements Classifiable {
// String classification defined in child classes
}
public class Car extends Automobile {
public String classification() { return "Car"; }
}
public class Bus extends Automobile {
public String classification() { return "Bus"; }
}
public class AutoMap {
Map<String,List<Automobile>> theMap = new Map<String,List<Automobile>>();
public AutoMap() { }
public void add(Automobile a) {
String type = a.classification();
List<Automobile> list = theMap.get(type);
if(list == null) {
list = new LinkedList<Automobile>();
theMap.put(type,list);
}
list.add(a);
}
public List<Automobile> getAutosOfType(String type){
return theMap.get(type);
}
}
public static void main(String[] args) {
List<Automobile> autos = getRandomAutos(); // defined somewhere? :)
AutoMap theAutomap = new AutoMap();
}
If you don't mind using the class name, it's as simple as:
public void <T extends EntityProxy> put(T o)
{
HashMap map = indexMap.get(o.getClass().getName());
if (map == null)
{
map = new HashMap();
indexMap.put(o.getClass().getName(), map);
}
map.put(o.getId(), o);
}
This code will create the required sub-hashmaps as you go along.
If you use getClass().getName() you get names on the form com.mypackage.Bus. If you're willing to handle name collisions and only want the simple name (in this case "Bus"), use getClass().getSimpleName() instead.
Try:
Map<Class<?>, Map<Long, EntityProxy>> indexMap = new HashMap<Class<?>, Map<Long, EntityProxy>>();
and
public void put(EntityProxy entityProxy){ // generics don't add anything here
...
(I didn't test it)