Which Java generic should be used in ambiguous cases? - java

I'm having some problems with a Wicket 1.3 -> Wicket 1.4 migration, but this question could be applied to Java generics overall, too. The migration has caused hundreds of warnings to spring up out of nowhere -- for those unfamiliar with Wicket, many Wicket classes are derived from a common ancestor, which became generified in v1.4 -- and I'm not sure what parameters to apply in some cases, mostly assorted forms and tables. I'm thinking they could do with <?>, <Object> or <Void>, but I'm not sure which.
<?> seems most appropriate to me, but there are many places where I can't use a wildcard. <Object> works in all cases, but it makes me uneasy because it's basically writing a wildcard without using the wildcard, which just feels inherently wrong to part of my brain. And using <Void> was suggested in the Wicket migration guide.
So what is the proper thing to do in this case?
EDIT 2: I think my first edit (now at the bottom of the question) confused people by making it seem like I was just asking about collections of strings. Here are other examples and their warnings:
public class DocumentProcessor extends Form implements DocumentManagement { ...
Form is a raw type. References to generic type Form should be parameterized
AjaxFallbackDefaultDataTable theTable = new AjaxFallbackDefaultDataTable("theTable", cols, dataProvider, recPerPg);
Multiple markers at this line
- Type safety: The constructor AjaxFallbackDefaultDataTable(String, List, ISortableDataProvider, int) belongs to the raw type AjaxFallbackDefaultDataTable. References to generic type AjaxFallbackDefaultDataTable should be parameterized
- AjaxFallbackDefaultDataTable is a raw type. References to generic type AjaxFallbackDefaultDataTable should be parameterized
- AjaxFallbackDefaultDataTable is a raw type. References to generic type AjaxFallbackDefaultDataTable should be parameterized
EDIT: I was hoping to make the question so broad it didn't need sample code, but here is some.
List<IColumn> columns = new ArrayList<IColumn>();
columns.add(new PropertyColumn(new Model<String>("Number"), "revisionID"));
These warnings are generated:
Multiple markers at [the first] line
- IColumn is a raw type. References to generic type IColumn should be parameterized
- IColumn is a raw type. References to generic type IColumn should be parameterized
Multiple markers at [the second] line
- Type safety: The constructor PropertyColumn(IModel, String) belongs to the raw type PropertyColumn. References to generic type PropertyColumn should be parameterized
- PropertyColumn is a raw type. References to generic type PropertyColumn should be parameterized
There are no errors.

Use Void if you're not going to make use of the component's underlying model object.
Semantically, it is more sound and conveys better the idea that it's not that the model object can be anything, but that it is semantically nothing and will never be used. The Void keyword is mostly used as the conventional solution in this kind of situation.
If you're going to use the model object and don't care, which I don't think is what you meant, **use wildcards where you can** and, where you can't (constructor arguments, etc), either Void, Object, or possibly some other "encompassing" class, making a decision based on your component's specific semantics and desired generic-typing behavior (as an example, in the case of a constructor for a component variable, you'd think about what your constructor will do with a Void or an Object typing).
Of course, that's in terms of "good programming practices" theory, in practice you don't have to care much, though this kind of thinking can help your teammates maintain your code, and can help you better understand it, maybe even predict bugs.
Using wildcards everywhere is fairly common among wicket users, perhaps even more common than deciding as I suggested, but that's not because wildcards are the convention, but is most likely simply due to most code examples that pop up in search engines preferring wildcards. Nonetheless, as the migration guide's suggestion of Void shows, not only are wildcards less semantically coherent, but also they don't seem to be an absolute convention, even challenged, it would seem, by wicket's developers, who, we'd better assume, know enough about their types' inner workings for their recommendations to be taken seriously.

Alternatives available would be:
To just use the raw type, as you have in the sample code, simply ignore the warnings
To use the wildcard/Object generic
To use an extends generic
I am assuming from your question that #1 is not a viable option for you.
Example for #2 (wildcard/Object)
List<IColumn<?>> columns = new ArrayList<IColumn<?>>();
OR
List<IColumn<Object>> columns = new ArrayList<IColumn<Object>>();
IMO I don't think it really matters whether you choose ? or Object, and neither one is more correct than the other, at least functionally.
If you don't care what the generic is, and you never access it, then it is of nigh consequence; although think ahead carefully, if indeed it is possible you would use generics here in the future. This will likely be the case only where in your pre-migration code, you found yourself not having to typecast anything from within the IColumn objects.
Example for #3 (extends generic)
Create a supertype or common interface to all the possible generics of the IColumn type. Where
T extends MyType:
List<IColumn<T>> columns = new ArrayList<IColumn<T>>();
I would base the decision in choosing, between the 2nd and 3rd method, on what the possible generic attributes for IColumn actually are.
If they are your own classes AND you actualy want to access objects of the generic type, I would go for the 3rd method,
otherwise, for example with String or boxed primitives such as Integer, or if you don't acees the objects of the generic type, I would go for method 2.
HTH

Semantically, using <?> means "I don't know the type, and I actually don't care at all. Using anything else sets expectations on the form of the expected content.
Practically, <Object> does the same, but states you'll use the properties of your generic that use the parameter type.
So the rule of thumb should be:
if you only work on the genetic object but not with it's parametrized content, use <?> so you know at first sight the parameter doesn't matter to the behavior.
in any other case, use the most specific parameter that encompasses all types your method is designed to work with. Extreme case is <Object>, other include <? extends SomeTopLevelType>

I haven't used wicket and Vodafone is blocking me from seeing API docs. However, it appears that you are missing many generic arguments and want something like:
List<IColumn<String>> columns = new ArrayList<IColumn<String>>();
columns.add(new PropertyColumn<String>(new Model<String>("Number"), "revisionID"));
If you want to add other IColumns with unrelated generic argument, you will need something like;
List<IColumn<?>> columns = new ArrayList<IColumn<?>>();
columns.add(new PropertyColumn<String>(new Model<String>("Number"), "revisionID"));
Or if you need to get at the column's properties, perhaps something like:
List<IColumn<String>> strColumns = new ArrayList<IColumn<String>>();
List<IColumn<?>> columns = new ArrayList<IColumn<?>>();
PropertyColumn<String> column =
new PropertyColumn<String>(new Model<String>("Number"), "revisionID");
strColumns.add(column);
columns.add(column);

You want to use the type of the model associated with your component. That is, use the type returned by a call to getModelObject(). So, to use an example from the migration guide:
ListView<Person> peopleListView = new ListView<Person>("people", people) {
protected void populateItem(ListItem<Person> item) {
item.add(new Link<Person>("editPerson", item.getModel()){
public void onClick() {
Person p = getModelObject();
setResponsePage(new EditPersonPage(p));
}
});
}
};
With generics it is easy to tell that that is a list of Persons, with a link to an edit page that uses a person as it's model. Unfortunately, very often in wicket your components won't have a model associated with them. In that case getModel() will return null so the proper type to use is <Void>, which is essentially a place holder for null.
DocumentProcessor
public class DocumentProcessor extends Form implements DocumentManagement { ...
if you aren't setting the model for DocumentProcessor it would look like so:
public class DocumentProcessor extends Form<Void> implements DocumentManagement {
public DocumentProcessor(String id) {
super(id);
....
but with a model DocumentProcessor looks something like this:
public class DocumentProcessor extends Form<Document> implements DocumentManagement {
public DocumentProcessor(String id, Document doc) {
super(id, doc);
AjaxFallbackDefaultDataTable
Judging from it's constructors AjaxFallbackDefaultDataTable will likely store either an IColumn[] or List in it's model, but for your implementation you don't know or care so <?> is appropriate, the difference between this and DocumentProcessor is that you're extending Form and therefore do know and do care how it is using it's model.
IColumn
For the IColumn/PropertyColumn example, I'm going to assume that the revisionID field is a Long, then I would write it like so:
List<PropertyColumn> columns = new ArrayList<PropertyColumn>();
columns.add(new PropertyColumn<Long>(new Model<String>("Number"), "revisionID"));
You might look at
More 1.4 Migration info
Void type parameter

The warnings say that IColumn interface and PropertyColumn class are parameterized types, so you just need to define type parameters for them.
Consider the following example:
List<Set> list = new ArrayList<Set>();
List and ArrayList are parameterized types and their type parameters are defined. However, Set is parameterized type as well but it's raw version is used as a type parameter, hence, compiler generates a warning at that case.
We can fix our example by explicitly specifying all type arguments, e.g.
List<Set<Integer>> list1 = new ArrayList<Set<Integer>>();
List<Set<String>> list1 = new ArrayList<Set<String>>();
You need to do the same thing for your generic classes and interfaces.

Related

Using Raw types to dynamically apply type parameters for a reflected subclass

IMPORTANT:
the code I currently have Is working per my expectations. It does what I want it to do. My Question is about wether the WAY in which I have made it work is wrong. The reason I am asking this is because I've seen plenty of stack overflow results about raw types and how they should basically NEVER be used.
What I'm doing and Why I used raw types
Currently I am dynamically creating a concrete subclass of a generic interface where the interface takes in parameters when the class is constructed. When I make an instance of this class and use its returned object to call various methods, I use raw types because it works for what I'm trying to do. Here is an example in my functioning code where the raw types are used. This code is in top down order i.e. between code blocks there is no code.
Loading properties file
Properties prop = new Properties();
try {
prop.load(ObjectFactory.class.getResourceAsStream("config.properties"));
This is the File parser that implements FileParserImplementation and takes in the data and puts it into an array. This code gets the Class type and then makes an instance of that type dynamically.
Class<? extends FileParserImplementation> parser = null;
parser = Class.forName(prop.getProperty("FileParserImplementation")).asSubclass(FileParserImplementation.class);
FileParserImplementation ParserInstance = (FileParserImplementation) parser.getDeclaredConstructors()[0].newInstance();
These two classes and their instances are the two seperate DataParsers implementing DataParserImplementation. These take in the array of Strings that the FileParser gives and creates objects/manipulates the data into whatever is needed. It puts out a Collection of this data. The Fileparser dependency is passed in through constructor injection. This can be configured through the properties file at runtime.
Class<? extends DataParserImplementation> dataset1 = Class.forName(prop.getProperty("DataParserImplementation_1")).asSubclass(DataParserImplementation.class);
Class<? extends DataParserImplementation> dataset2 = Class.forName(prop.getProperty("DataParserImplementation_2")).asSubclass(DataParserImplementation.class);
DataParserImplementation Dataset1Instance = (DataParserImplementation) dataset1.getDeclaredConstructors()[0].newInstance(ParserInstance);
DataParserImplementation Dataset2Instance = (DataParserImplementation) dataset2.getDeclaredConstructors()[0].newInstance(ParserInstance);
This is the Crossreferencer class that implements CrossReferencerImplementation. It takes in the two datasets and Cross references them In whatever way is desired by the actual concrete reflected class. This also can be configured at runtime. It outputs a Map in this main.
The map serves as the final collection for the data (I might change that later).
Class<? extends CrossReferenceImplementation> crossreferencer = Class.forName(prop.getProperty("CrossReferenceImplementation")).asSubclass(CrossReferenceImplementation.class);
CrossReferenceImplementation crossReferencerInstance =
(CrossReferenceImplementation) crossreferencer.getDeclaredConstructors()[0].newInstance();
Getting the Map result from calling a method on our reflected instance. Then the contents of this map are printed out. currently it seems the map parameters are gotten as well because the Objects that are inside the map are properly using their toString methods when reflectiveFinalMap.get(key).toString() is called.
This leads me to believe it works as I intend.
Map reflectiveFinalMap = (Map)
crossReferencerInstance.CrossReference(Dataset1Instance.Parse(), Dataset2Instance.Parse());
for (Object key:reflectiveFinalMap.keySet()) {
System.out.println(key + " { " +
reflectiveFinalMap.get(key).toString() + " }");
}
return reflectiveFinalMap;
}
//catch block goes here
Notice that each time I reflectively create an instance of a class that implements one of my interfaces, I use the interface as the raw type. My Hope is that the reflection then sees the parameterized type of this raw type when it creates the concrete subclass, because thats where the parameter types are actually specified. The point is to let any class that implements those interfaces be generic to the point where they can take in just about anything and return just about anything.
Things I tried to not use raw types.
I've tried to actually obtain the parameterized type of CrossReferenceImplementation in the reflected crossreferencer Class that I get right now by calling
Class arrayparametertype = (Class)((ParameterizedType)crossreferencer.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
And then I tried to pass in that arrayparameter when creating an instance of crossreferencer like this:
CrossReferenceImplementation crossReferencer = (CrossReferenceImplementation<<arrayparametertype>>) crossreferencer.getDeclaredConstructors()[0].newInstance();
That didn't work since variable parameter types apparently aren't a thing.
I tried to manually specify the specific parameter of the concrete reflected class(I DON'T want this anyway because it breaks the whole point of reflection here, decoupling the Classes from each other by being able to use anythng that implements the appropriate interface). This caused this warning to appear and the code to not actually run the methods it was supposed to:
//how the parameters were specified. Messy and breaks the reflection.
CrossReferenceImplementation<Map<String, SalesRep>,Map<String, SalesRep>,Map<String, SalesRep>> crossReferencer = (CrossReferenceImplementation) crossreferencer.getDeclaredConstructors()[0].newInstance();
//where the warning occured
Map reflectiveFinalMap = (Map) crossReferencer.CrossReference(Dataset1.Parse(), Dataset2.Parse());
The Warning:
"Dataset1 has raw type so result of Parse is erased".
Note that SalesRep here is the object in which the data is held as fields of that object. This object gets manipulated and put into various collections. It too is accessed via reflection in the many methods of DataParserImplementations
A similar error message and problem occured when specifying the parameter type of the Map (AGAIN I DON'T want this because it makes the reflection pointless I want the map return result to be generic and be specified by the implementing class).
//where the parameterized type was specified
Map reflectiveFinalMap = (Map<String,SalesRep>) crossReferencer.CrossReference(Dataset1.Parse(), Dataset2.Parse());
When specifying the actual parameterized type of the map result the error message was:
"crossReferencer has raw type so result of CrossReference is erased".
Running the code did indeed confirm for me that .CrossReference method's results were erased while everything else ran fine.
What internet searches I tried before asking here
So I used the raw types for both operations As can be seen in the main code and everything worked fine. But I have seen so much "Don't use raw types". And this is why I ask: Is this an appropriate use of raw types? Should I do it a different way that DOESN'T break the reflection? It breaks the reflection because manually specifying the type parameter not only makes my code not run, it also means ONLY that concrete class can be used. I reflected so that I could use anything that implements the generic interface. I don't want to only be able to use specific concrete instances. I've tried searching stack overflow for whats in my title and other similar things. I think this might be related to type erasure but I'm honestly not sure of that. Nothing else really addressed this problem because nothing talked about generics, parameterized types and reflection all at once (the crux of my problem). I have been told generics and reflection don't play well together but this code works anyways and works the way I want it to. It works well. I just want to make sure I'm not doing something TERRIBLY wrong.
The Goal.
To gain an Understanding of my current usage of raw types so I know I'm doing it the right way. By 'Right' I mean the opposite of what I define as the 'Wrong' Way below. An example of what 'Understanding' I seek is:
To understand why puesdo code along the lines of:
ConcreteClass forname(myPropertiesFileObject.get(ConcreteClassname)) as subClass of (MyGenericInterface);
MyRAWGenericInterfaceType ConcreteClassInstance = (MyRAWGenericInterfaceType) ConcreteClass.newInstance( Insert generic Type constructor arguments here);
RAWCollectionType someCollection = RAWCollectionType concreteClassInstance.CallingAMethod(Insert generic Type method arguments here);
Uses Raw types where RAW is contained in the Interface or collection type name. This is as opposed to doing it in some way that doesn't use raw types but doesn't break the point of the reflection, to decouple the interactions between these classes. Specifying the parameters with hard code would 'break the reflection' in this case. Additionally I'd like to understand Why specifying parameters (even if I know thats not what I'm going to do) for these RAW types in the pusedocode above causes the errors listed above in the question, Namely why is the result of CallingAMethod erased when supplying the actual parameters to the RAWCollectionType that the method returns? The root problem is that when I supply type parameters to RAWCollectionType when I declare it, it refuses to be updated by what CallingAMethod returns and I Don't Understand Why. It takes the return value, but if the body of the method CallingAMethod has the returned value passed in as an argument, updated inside the method and then returned, the return that I receive doesn't have the updates. CallingAMethod in this example would be like if I hada list like:
[1,2,3]
and inside the method I had something like:
foreach(thing in list){
thing += 1
}
and then I returned the list, the return I'd get when specifying parameters would be [1,2,3] and when using raw types it would be [2,3,4] like I desire. I'm asking this because I've heard bad things about using raw types.
Additionally I want to make sure that my use of raw types is not horribly wrong and that it works because it's SUPPOSED to work. Maybe I've just gotten good at the whole reflection and generics thing and found a valid use for raw types, or I could be doing something so horrible it warrants my arrest. Thats what i intend to find out. To clarify, by wrong I mean:
bad design (should use a different way to call my methods reflectively and also use reflective classes that use generic interfaces)
inefficient design(time complexity wise, code line wise or maintainability wise)
there is a better way, you shouldn't even be doing this in the first place
If any of those reasons or something I missed popped out when you read this code then TELL ME. Otherwise please explain then why my use of raw types is Valid and isn't a violation of this question:[link]What is a raw type and why shouldn't we use it?
Java have type erasure, so your Map<A,B> in runtime is just a Map, same for CrossReferenceImplementation<Map<String, SalesRep>,Map<String, SalesRep>,Map<String, SalesRep>> is just a CrossReferenceImplementation.
This also means that you can cast any map to Map and just put any objects you want in it, so you can have Map<String, Long> that is actually storing objects of Map<Cookie, Fish> type, and this is why you need to be careful with raw types and reflections.
You can't really use reflection and generics normally - you will always have some unchecked code then, but you can limit it to minimum and make it kind of type-safe anyways.
Like you can create own method to get field: (this is a bit of pseudocode, I will skip all possible exceptions, etc)
public class FieldAccessor<O, T> {
final Field field; // + private constructor
public T get(O object) { return (T) field.get(object); } // unsafe, bu we validated this before constructing this accessor
public static <O, T> FieldAccessor<O, T> create(Class<? super O> definingClass, Class<? super T> fieldClass, String fieldName) {
Field field = definingClass.getDeclaredField(fieldName);
if (field.getType() != fieldClass) {
throw some exception;
}
return new FieldAccessor<>(field);
}
Then you have all the needed validation before you need to use that field, and it will already return valid type. So you can get some value of valid type and add it to normal generic Map instance.
FieldAccessor<X, A> keyAccessor = FieldAccessor.create(X.class, A.class, "someProperty");
FieldAccessor<Y, B> valueAccessor = FieldAccessor.create(Y.class, B.class, "someOtherProperty");
Map<A, B> myMap = new HashMap<>();
mapMap.put(keyAccessor.get(myXValue), valueAccessor.get(myYValue));
This way you have type safe code that still works on reflections - it might still fail at runtime if you will provide invalid types, but at least you always know where it will fail - as here FieldAccessor is already checking all the types in runtime to ensure that you will not do something stupid like add Integer to Map<String, Long> as this might be hard to debug later. (unless someone will use this accessor as raw type, as .get isn't validated - but you can add that by passing definingClass to constructor and checking object instance in get methods)
You can do similar stuff for methods and fields that use generic types (like field of Map<X, Y> type, this FieldAccessor would only allow you to check if it is some kind of Map) - but it would be much harder as API for generics is still a bit "empty" - there is no build in way to create own instances of generic types or to check if they are assignable. (libraries like gson does that so they can deserialize maps and other generic types, they have own implementation of java generic type representation interfaces, like that ParameterizedType and implemented own method to check if given types are assignable)
Just when you are using reflections you need to always remember and understand that you are the one responsible for validating types, as compiler can't help you here, so that unsafe and raw typed code is fine as long as you have logic that validates if this code will never do something really unsafe (like that passing wrong type to generic method, like Integer to map of Long).
Just don't throw raw types and reflections in the middle of some normal code, add some abstraction to it, so it will be easier to maintain such code and project.
I hope this somewhat answers your question.

How do I get the class of a parameterized type in Java without instantiating it? [duplicate]

If I am creating a java class to be generic, such as:
public class Foo<T>
How can one determine internally to that class, what 'T' ended up being?
public ???? Bar()
{
//if its type 1
// do this
//if its type 2
// do this
//if its type 3
// do this
//if its type 4
// do this
}
I've poked around the Java API and played with the Reflection stuff, instanceof, getClass, .class, etc, but I can't seem to make heads or tails of them. I feel like I'm close and just need to combine a number of calls, but keep coming up short.
To be more specific, I am attempting to determine whether the class was instantiated with one of 3 possible types.
I've used a similar solution to what he explains here for a few projects and found it pretty useful.
http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-java/
The jist of it is using the following:
public Class returnedClass() {
ParameterizedType parameterizedType = (ParameterizedType)getClass()
.getGenericSuperclass();
return (Class) parameterizedType.getActualTypeArguments()[0];
}
In contrast to .NET Java generics are implemented by a technique called "type erasure".
What this means is that the compiler will use the type information when generating the class files, but not transfer this information to the byte code. If you look at the compiled classes with javap or similar tools, you will find that a List<String> is a simple List (of Object) in the class file, just as it was in pre-Java-5 code.
Code accessing the generic List will be "rewritten" by the compiler to include the casts you would have to write yourself in earlier versions. In effect the following two code fragments are identical from a byte code perspective once the compiler is done with them:
Java 5:
List<String> stringList = new ArrayList<String>();
stringList.add("Hello World");
String hw = stringList.get(0);
Java 1.4 and before:
List stringList = new ArrayList();
stringList.add("Hello World");
String hw = (String)stringList.get(0);
When reading values from a generic class in Java 5 the necessary cast to the declared type parameter is automatically inserted. When inserting, the compiler will check the value you try to put in and abort with an error if it is not a String.
The whole thing was done to keep old libraries and new generified code interoperable without any need to recompile the existing libs. This is a major advantage over the .NET way where generic classes and non-generic ones live side-by-side but cannot be interchanged freely.
Both approaches have their pros and cons, but that's the way it is in Java.
To get back to your original question: You will not be able to get at the type information at runtime, because it simply is not there anymore, once the compiler has done its job. This is surely limiting in some ways and there are some cranky ways around it which are usually based on storing a class-instance somewhere, but this is not a standard feature.
Because of type erasure, there is no way to do this directly. What you could do, though, is pass a Class<T> into the constructor and hold onto it inside your class. Then you can check it against the three possible Class types that you allow.
However, if there are only three possible types, you might want to consider refactoring into an enum instead.
The Problem is that most of the Generic stuff will disappear during compilation.
One common solution is to save the type during the creation of the Object.
For a short introduction in the Type Erasure behaviour of java read this page
If you know a few specific types that are meaningful, you should create subclasses of your generic type with the implementation.
So
public class Foo<T>
public ???? Bar()
{
//else condition goes here
}
And then
public class DateFoo extends Foo<Date>
public ???? Bar()
{
//Whatever you would have put in if(T == Date) would go here.
}
The whole point of a generic class is that you dont need to know the type that is being used....
It looks like what you want is in fact not a Generic class, but an interface with a number of different implementations. But maybe it would become clearer if you stated your actual, concrete goal.
I agree with Visage. Generics is for compile-time validation, not runtime dynamic typing. Sounds like what you need is really just the factory pattern. But if your "do this" isn't instantiation, then a simple Enum will probably work just as well. Like what Michael said, if you have a slightly more concrete example, you'll get better answers.

Generics and raw types

I wrote some code using generics and I got into the following situation I didn't manage to understand:
I have the interface IpRange, and the following class:
public class Scope<IpRange<T extends IP>> {
List<IpRange<T>> rangesList;
public List<IpRange<T>> getRangesList() {return rangesList;}
}
Now from some test class if i write the following:
Scope<Ipv4> myScope = new Scope<Ipv4>();
scope.getRangesList().get(0)
I'm getting object of IpRange type, but if I'm using a raw type and doing this:
Scope myScope = new Scope();
scope.getRangesList().get(0)
I'm getting Object, and I can't use the ipRange methods unless i explicitly cast it to Range.
If it would have been List<T> i get it, since i used raw type the compiler has no way to know what is the actual type of the list items, but in this case it will be always IpRange type, so why I'm not getting Object?
The thing is that when I'm creating the scope I don't necessarily know the actual range type. Consider this constructor: public Scope(String rangeStringList); for all I know, the string could be "16.59.60.80" or "fe80::10d9:159:f:fffa%". But what I do know is that I passed some IpRange object to the compiler and I would expect to be able to use this interface whether this is ipv4 or ipv6. And since the compiler can know for sure that this is ipRange even if I used row type, i wonder why java chose to do it this way
People have pointed out that all generic type information is stripped when using raw types, and hinted that this is to do with backwards compatibility. I imagine this might not be satisfactory without an explanation, so I'll try to explain how such a problem might be encountered with code like yours.
First of all, imagine the code you have written there is part of an old library, and you're in the process of upgrading the library by adding generics. Perhaps it's a popular library and lots of people have used the old code.
Someone may have done something like this using the classes from your library:
private void someMethod(Scope scope, Object object) {
scope.getRangesList().add(object);
}
Now, looking at this we know that Object might not be of the type IpRange, but this is a private method, so let's assume that type checking is effectively performed by whatever methods call someMethod. This might not be good code, but without generics it does compile and it might work just fine.
Imagine that the person who wrote this upgraded to the new version of your library for some new features or unrealted bug fixes, along with this they now have access to more type safety with your generic classes. They might not want to use it, though, too much legacy like the extract above code using raw types.
What you are effectively suggesting is that even though 'scope' is a raw type, the List returned from getRangesList() must always be of type List<IpRange<? extends IP>>, so the compiler should notice this.
If this were the case though, the legacy code above which adds an Object to the list will no longer compile without being edited. This is one way backwards compatibility would be broken without disregarding all available generic type information for raw types.
Yes, if you use raw types, all generics are "turned off" in the rest of that method, and all generic types become raw types instead, even if they would otherwise not be affected by the missing generic parameter of the raw type.
If you use a raw type, all generic type information is stripped from the class, including static methods if called on the instance.
The reason this was done was for backward compatibility with java 1.4.

What are the 'good' reasons to use <?> in Java Generics?

Now and then in my code I find that I can solve a problem by either using a naked generic class or one with wildcards.
I have a design where a class like this:
Class World<T, C> { .... }
definitely in general is making my code cleaner than it would have been without generics.
Yet, sometimes I use
World theWorld;
or sometimes I end up with
World<?, ?> theWorld;
I do this because it seems to be what it takes to make the compiler accept it and my
attempts to avoid them lead me to more casting or inheriting complexity.
It looks ugly and smelly to me and yet I cannot justify the added complexity it looks like I need to introduce to avoid it.
What are some cases (if any) that you believe using a naked or wildcarded generic is acceptable idiomatic Java?
There is a good example (use case) of using <?> in the Wildcards section of the Generics tutorial.
A sort summary: if you want to write a printCollection method which accepts all kinds of Collection you could use Collection<?> as a parameter type. You cannot use Collection<Object> as the parameter type for this purpose because it is not a supertype of all kinds of collections.
Using Collection<?> instead of "pure" Collection in this case is safer because you cannot add items to Collection<?> (except for null) in the printCollection method.
Any time you could use generics but don't need it in that specific situation. <?> tells the compiler roughly: "I know about generics, but I don't need the type now".
Maybe the type is needed in other situation. E.g. if you have a Set that just stores anything, like a cache, and you just don't care for the type of the elements at all. At other times you do, when you process specific elements.
It smells if you use too loosely bound type parameters (like <?> which is quite loose) but try to determine the type afterwards, e.g. with instanceof or some custom type discriminator. Then something was designed poorly.
The Wildcard also proves as very useful, when you want to store multiple objects, that are a subclass of another class.
For example:
Collection<? extends Shape> myShapes
This Collection now could keep all the objects, that are a subclass of Shape.
So you are type-safe when adding any object that is a subclass of Shape. For Example:
myShapes.add(new Rectangle())
myShapes.add(new Triangle())

Java Generic Class - Determine Type

If I am creating a java class to be generic, such as:
public class Foo<T>
How can one determine internally to that class, what 'T' ended up being?
public ???? Bar()
{
//if its type 1
// do this
//if its type 2
// do this
//if its type 3
// do this
//if its type 4
// do this
}
I've poked around the Java API and played with the Reflection stuff, instanceof, getClass, .class, etc, but I can't seem to make heads or tails of them. I feel like I'm close and just need to combine a number of calls, but keep coming up short.
To be more specific, I am attempting to determine whether the class was instantiated with one of 3 possible types.
I've used a similar solution to what he explains here for a few projects and found it pretty useful.
http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-java/
The jist of it is using the following:
public Class returnedClass() {
ParameterizedType parameterizedType = (ParameterizedType)getClass()
.getGenericSuperclass();
return (Class) parameterizedType.getActualTypeArguments()[0];
}
In contrast to .NET Java generics are implemented by a technique called "type erasure".
What this means is that the compiler will use the type information when generating the class files, but not transfer this information to the byte code. If you look at the compiled classes with javap or similar tools, you will find that a List<String> is a simple List (of Object) in the class file, just as it was in pre-Java-5 code.
Code accessing the generic List will be "rewritten" by the compiler to include the casts you would have to write yourself in earlier versions. In effect the following two code fragments are identical from a byte code perspective once the compiler is done with them:
Java 5:
List<String> stringList = new ArrayList<String>();
stringList.add("Hello World");
String hw = stringList.get(0);
Java 1.4 and before:
List stringList = new ArrayList();
stringList.add("Hello World");
String hw = (String)stringList.get(0);
When reading values from a generic class in Java 5 the necessary cast to the declared type parameter is automatically inserted. When inserting, the compiler will check the value you try to put in and abort with an error if it is not a String.
The whole thing was done to keep old libraries and new generified code interoperable without any need to recompile the existing libs. This is a major advantage over the .NET way where generic classes and non-generic ones live side-by-side but cannot be interchanged freely.
Both approaches have their pros and cons, but that's the way it is in Java.
To get back to your original question: You will not be able to get at the type information at runtime, because it simply is not there anymore, once the compiler has done its job. This is surely limiting in some ways and there are some cranky ways around it which are usually based on storing a class-instance somewhere, but this is not a standard feature.
Because of type erasure, there is no way to do this directly. What you could do, though, is pass a Class<T> into the constructor and hold onto it inside your class. Then you can check it against the three possible Class types that you allow.
However, if there are only three possible types, you might want to consider refactoring into an enum instead.
The Problem is that most of the Generic stuff will disappear during compilation.
One common solution is to save the type during the creation of the Object.
For a short introduction in the Type Erasure behaviour of java read this page
If you know a few specific types that are meaningful, you should create subclasses of your generic type with the implementation.
So
public class Foo<T>
public ???? Bar()
{
//else condition goes here
}
And then
public class DateFoo extends Foo<Date>
public ???? Bar()
{
//Whatever you would have put in if(T == Date) would go here.
}
The whole point of a generic class is that you dont need to know the type that is being used....
It looks like what you want is in fact not a Generic class, but an interface with a number of different implementations. But maybe it would become clearer if you stated your actual, concrete goal.
I agree with Visage. Generics is for compile-time validation, not runtime dynamic typing. Sounds like what you need is really just the factory pattern. But if your "do this" isn't instantiation, then a simple Enum will probably work just as well. Like what Michael said, if you have a slightly more concrete example, you'll get better answers.

Categories

Resources