5

I have a method which returns a List<Property<?>>.

Property is a type having one generic parameter:

public class Property<T extends Comparable<T>> { ... }

Having a list of mixed-typed properties, I cannot know what type parameter a specific element has.

I would like to do something like that:

List<Property<?>> list = getList();
for(Property<?> crt : list)
{
    PropertyWrapper<?> crtWrapper = new PropertyWrapper(crt.getGenericType());
    //                        I know this doesn't exist ----^
}

In one sentence: I need the PropertyWrapper to have the same generic template argument as the current Property does. Is there any way to do this?

I could apply a suggestion as stated in https://stackoverflow.com/a/3437930/146003 but even if I do this, how to instanciate the appropriate PropertyWrapper<XXX> then, only having an instance of Class<T>?

I can modify Property<?> if required. I also don't mind if reflection needs to be used (I assume it needs to be)


EDIT: I forgot something. In fact I cannot instanciate the wrapper by the line

PropertyWrapper<?> crtWrapper = new PropertyWrapper(crt.getGenericType());

because I have specialized subclasses (PropertyWrapper_String).

Now I see two possibilities:

1: Instanciate the class by string:

String strGenericType = "";
Class<?> wrapperClass = Class.forName("PropertyWrapper_" + strGenericType);

2: Is there any way to specialize a generic class without creating a subclass?


Many thanks in advance for your tips

Community
  • 1
  • 1
Atmocreations
  • 9,923
  • 15
  • 67
  • 102
  • can we assume that when you wrote `new PropertyWrapper(crt.getGenericType());` you meant `new PropertyWrapper();` – John B Feb 06 '12 at 13:14
  • @JohnB no, I assume he wants to pass the class type of the generic parameter to the constuctor, much like in the other question he linked. – Thomas Feb 06 '12 at 13:18
  • But if he is creating a generic type `PropertyWrapper>` seems to make sense that the constructor would use the `<>` operator. – John B Feb 06 '12 at 13:29
  • @JohnB yes, it would make sense to use that, but if you have `PropertyWrapper( Class type)` you don't need the `<>` in the constructor, just pass the class object as a parameter. – Thomas Feb 06 '12 at 13:31
  • Maybe you can find some inspiration here: http://stackoverflow.com/questions/9111899/how-do-i-build-a-java-type-object-at-runtime-from-a-generic-type-definition-and . But even there no solution was found, only a workaround to achieve the OP's real goal. – Hauke Ingmar Schmidt Feb 06 '12 at 13:38
  • @JohnB: You're right. The generic parameter should be what `getGenericType()` would return if it existed. – Atmocreations Feb 06 '12 at 14:08
  • Okay. I did it the way with passing an instance of xxx.class to the property-class. It works but I think it's ugly. Hoping that there's someone having some better solution. – Atmocreations Feb 06 '12 at 15:12
  • 1
    You could do a mapping of `Map, Class>>` where the key is the class being wrapped `String` and the value is the wrapper `PropertyWrapper_String` – John B Feb 06 '12 at 17:42
  • @JohnB I really like the map approach, in fact we use that a lot (e.g. for registering string-to-someclass converters, where _someclass_ is the key and the converter class is the value). Pls, add that to your answer to make it easier to find. – Thomas Feb 07 '12 at 07:36
  • `Is there any way to specialize a generic class without creating a subclass?` - You can't specialize classes that way, just instances. However, since instances don't have runtime reflection information other than the class they are of, you'd have to store the generic type class in the instance - like all our answers already pointed out. – Thomas Feb 07 '12 at 07:38
  • Posted the update to my answer. – John B Feb 07 '12 at 11:27

4 Answers4

7

Try creating the following method:

<T> PropertyWrapper<T> createWrapper(Property<T> property){
      return new PropertyWrapper<T>(property);
}

Then call it as such.

List<Property<?>> list = getList();
for(Property<?> crt : list)
{
    PropertyWrapper<?> crtWrapper = createWrapper(crt);
}

The reason the above works is that the generic type T is inferred from the argument and is locked down for the entire method. Unlike using <?> in the loop where each instance of <?> is inferred to be a different type.

Edit:

To deal with the issue of having a different class type depending on the class in the wrapper, consider a Map where the key is the class being wrapped and the value is the wrapper.

Map<Class<?>, Class<?>> myMap;

Then you could so something like this:

Class<?> wrappedType = property.getGenericType();
Class<?> wrapperClass = myMap.get(wrappedType);
PropertyWrapper<?> wrapper = (PropertyWrapper<?>) wrapperClass.newInstance();

Although you might need to do something like this if you need to pass an argument.

Constructor<?> constructor = wrapperClass.getDeclaredConstructor(property.getClass());
PropertyWrapper<?> wrapper = (PropertyWrapper<?>) constructor.newInstance(property);
John B
  • 32,493
  • 6
  • 77
  • 98
  • Thanks, John. Your idea is simple and would work, but... I modified my question because I didn't think that this wouldn't work this way. Any idea? – Atmocreations Feb 06 '12 at 14:48
1

If you have a Class<T> you can just take that class, create an instance by calling newInstance() on the class object and cast it to T.

The problem you have is getting the generic parameter of crt. If you have concrete subclasses of Property, e.g. StringProperty extends Property<String>, you can get the type using reflection. However, if you only create instances of Property<T> without concrete subclasses and you don't know where the elements of the list are created, AFAIK it is impossible to get the generic type (even if you know where the elements are created it might be impossible though).

Thus, the only way you might get the property wrapper to know the type of the property might be to store the type parameter (the class) in the property itself. Then the wrapper could query the property for its type/class member variable.

Edit: some explanation on why this is impossible or at least very hard.

The problem with generics is that due to type erasure the generic type is lost when you create a property using new Property<SomeType>(). There's just no runtime information that you could use to retrieve SomeType here.

If you have concrete subclasses (defining concrete generic types) you have reflection information available at runtime of what where the generic parameters of each class. Then you could get the actual class of each property and retrieve the reflection data for that class.

This would also be possible if you have methods or fields that define those types and return/hold references to a propery. However, I doubt you have that information since you seem to get some list and don't know exactly on where and how the elements of that list were created.

I further assume the properties' class is Property only, not a subclass. Thus the only way is to provide the runtime information yourself, i.e. by passing a reference to the type class as a constructor parameter.

Thomas
  • 87,414
  • 12
  • 119
  • 157
  • I don't know why, what you wrote is correct. Maybe because you didn't answer the question, only saying: "What you want is technically impossible". I would even be more drastic with wording: `new Property()` is the same as `new Property()` at runtime so you neither can get the type nor instantiate a parametrized type at runtime. But I have burnt my fingers with this topic also. – Hauke Ingmar Schmidt Feb 06 '12 at 13:37
  • @his thanks for the confirmation :). One of the questions was `Is there any way to do this?` so my short answer would be: no - and this should answer all the questions, the rest is just a bit more explanation :) – Thomas Feb 06 '12 at 13:42
0

Okay I'm going to answer myself.

I'm now passing an instance of Class<?> to the Property-class.

Then I extract the basic name of the property and simply cut away "java.lang." which is possibly as in most cases, I'm doing this to primitive data types - resp. their autoboxing classes.

Further, I just instanciate a new instance of the wrapper by name and pass the to be wrapped property as a parameter to the constructor which applys for that.

Here some code for the interested ones among you:

String template = "..."; // some package definition
for (Property<?> crt : bag)
{
    String className = template + crt.getClassName();
    Class<? extends PropertyWrapper<?>> wrapperClass = null;
    wrapperClass = (Class<? extends PropertyWrapper<?>>) Class.forName(className);
    Constructor<? extends PropertyWrapper<?>> constructor = wrapperClass.getConstructor(new Class<?>[] {Property.class});
    PropertyWrapper<?> wrapper = constructor.newInstance(crt);
    // Further operations using the wrapper
}

for simplicity, I left out the error handling part.

Atmocreations
  • 9,923
  • 15
  • 67
  • 102
  • Hmm, I'm not sure I like that solution. That would not really be refactoring safe (e.g. when changing the package you might forget to adapt the template etc.) and all that fiddling with strings seems somewhat fragile (and it seems as if the wrappers have the same simple name as the properties, don't they?). Why don't you use the map approach @JohnB suggested in a comment and map `String.class` to `StringWrapper.class` etc. ? That would remove the need to fiddle with strings and improve refactoring safety. – Thomas Feb 07 '12 at 07:29
  • This further would require you to have a wrapper class for each property type, i.e. a string wrapper, an integer wrapper etc. This seems to contradict your question where you have `PropertyWrapper` only. - Btw, I still don't know what you need those wrappers for. – Thomas Feb 07 '12 at 07:32
0

Rather than instantiating the wrapper from the call site, why not have the Property know how to create its own wrapper? Something like:

public class Property<T extends Comparable<T>> {
    PropertyWrapper<T> newWrapper();
}

That only half-helps, though. Your real problem is that in a loop like yours:

List<Property<?>> list = getList();
for(Property<?> crt : list)
{
    PropertyWrapper<?> crtWrapper = crt.newWrapper();
}

The fact that the PropertyWrapper and the Property have the "same" type isn't very helpful, since that type is just the unbound wildcard.

I really hate to give one of those "what are you really trying to do" answers, but -- what are you really trying to do? Generally speaking, once you get to an unbound wildcard, all hope is lost (unless you're willing to do unsafe, uncheckable casts or bend over backwards with reflection checks). One way around this is to put as much of the action within the Property<T> as possible,before you put that property in the List<Property<?>>. My newWrapper() is an example of this: it puts the action of creating a PropertyWrapper<T> into the Property<T> itself. If you wanted to register a callback for when the property changed, that's also something you may be able to do at each place you instantiate a non-wildcard Property<Whatever>. For instance:

Property<UserLogin> property = new Property<UserLogin>();
SomeListener<UserLogin> listener = whatever();
property.addListener(listener);
wildcardedPropertiesList.add(property);

This particular example probably won't help, but hopefully it'll give you applicable ideas.

yshavit
  • 42,327
  • 7
  • 87
  • 124