I was wondering if it is possible to create a construtor function:
public <C, T> C newInstance(Class<C> constructor_class,
Class<T> type,
Object...parameters){
// create a new Instance with the generic type and the paramters
//so basicly like this, but with generics and parameters
return constructor_class.newInstance();
}
You basically can't. Your example is broken - what is type supposed to represent?
Let's say you want to dynamically create a HashMap<String, Integer>. This would require that you pass in HashMap.class, String.class, and Integer.class, but we run into a TON of problems right off the bat:
How do we know that HashMap has 2 args? We don't, so you'd have to pass 2 class references via varargs and at runtime I guess we throw an exception if you passed the wrong number of classes.
What if you pass Map, which cannot be instantiated at all?
But most of all, this is completely useless, generics that do not show up in method/field/class signatures are figments of the compiler's imagination: javac uses it during compilation to warn or error, as well as to generate invisible caste operations, and then chucks this info away. By doing it dynamically, all that 'at compile time' stuff cannot work by definition (we're compiling, not running, the whole point is for the types to be dynamic, so javac cannot do), so to javac it is useless, and the info is then chucked. So that makes it fully useless, then, no?
Furthermore, Class cannot be used to convey generics args. What if I wanted a HashMap<List<String>, Integer> instead? List<String> does not exist as a class literal, so java.lang.Class<T> cannot be used here. There are also class objects (specifically, int.class) that aren't legal in generics. So, we have ?, ? super Foo, A, B extends A, C extends A & Serializable, List<String> all as examples of things that can go in <> but which cannot be a java.lang.Class reference, and int.class that is a legal class ref but which cannot go in <>. You can't put a square peg in a round hole. Let alone the fact that even if you could, it would do literally absolutely nothing whatsoever (see point #3: This is useless - most generics, and definitely the generics you'd use here, affect compilation and cease to exist afterwards).
There are crazy hacks in this space (super type tokens, for example), but if you do not fully understand how generics work under the hood, there is 0.00001% or less chance you will be able to use such hacks usefully, so don't bother until then. And asking this question, especially with that pseudocode, strongly suggests you don't understand enough of it yet.
Related
Let me preface this question by saying up front that I understand what Java can and can't do and am not asking about that. I'm wondering what the actual technical challenges are, from JVM and compiler standpoint, that require the compiler to behave the way it does.
Whenever I see discussions on weaknesses or most hated aspects of java Type Erasure always seems to be somewhere near the top of the list for Java Developers (it is for me!). If my history is correct Java 1.0 never implementing any type checking beyond passing Objects and recasting them. When a better Type system was required Sun had to decide between full Typing support which would break backwards comparability or going with their chosen solution of generics which didn't break old code.
Meanwhile C# ran into the same issue and went the opposite route of breaking backwards comparability to implement a more complex typing system around the same time (I believe).
My main question is why was this a either-or question for the two languages? What is it about the compiler process that means there is no way to support C# style handling of type without breaking backwards comparability in old code? I understand part of the problem is that the exact type is not always known at compile time, but at first (naive) glance it seems like some times it can be known at compile time, or that it can be left unknown at compile time and handled with a sort of reflection approach at runtime.
Is the problem that it's not feasible to implement, or that it was simply deemed too slow to implement a runtime sort of solution?
To go a step further lets use a simple generic factory example of code as an example of a place where type erasure feels rather cumbersome.
public class GenericFactory<FinalType, BuilderType<FinalType> extends GenericBuilder<FinalType>>{
private Class builderClass;
public GenericFactory(Class<BuilderType> builderClass){
this.builderClass=builderClass;
}
public FinalType create(){
GenericBuilder builder=builderClass.newInstance();
builder.setFoo(getSystemProperty("foo");
builder.setBar(getSystemProperty("bar");
builder.setBaz(getSystemProperty("baz");
return builder.build();
}
}
This example, assuming I didn't screw up on syntax somewhere, shows two particular annoyances of type erasure that at first glance seem like they should be easier to handle.
First, and less relevant, I had to add a FinalType parameter before I could refer to BuilderType extends GenericBuilder, even though it seems like FinalType could be inferred from BuilderType. I say less relevant since this may be more about generics syntax/implementation then the compiler limits that forced type erasure.
The second issue is that I had to pass in my BuilderClass object to the constructor in order to use reflection to build the builder, despite it being defined by the generics already. It seems as if it would be relatively easy for the compiler to store the generic class used here (so long as it didn't use the ? syntax) to allow reflection to look up the generic and then construct it.
Since this isn't done I presume there is a very good reason it is not. I'm trying to understand what these reasons are, what forces the JVM to stick with type erasure to maintain backwards compatibility?
I'm not sure what you're describing (the two "annoyances") are a result of type erasure.
I had to add a FinalType parameter before I could refer to BuilderType extends GenericBuilder, even though it seems like FinalType could be inferred from BuilderType
BuilderType<FinalType> would not be a valid generic type name unless I missed some changes to that in Java 8. Thus it should be BuilderType extends GenericBuilder<FinalType> which is fine. FinalType can't be inferred here, how should the compiler know which type to provide?
The second issue is that I had to pass in my BuilderClass object to the constructor in order to use reflection to build the builder, despite it being defined by the generics already.
That's not true. The generic parameters don't define what FinalType actually is. I could create a GenericFactory<String, StringBuilderType> (with StringBuilderType extends GenericBuilder<String>) as well as a GenericFactory<Integer, IntegerBuilderType> (with IntegerBuilderType extends GenericBuilder<Integer>).
Here, if you'd provide the type parameters to a variable definition or method call, type erasure would happen. As for the why refer to Andy's comment.
However, if you'd have a field or subclass, e.g. private GenericFactory<String, StringBuilderType> stringFactory, there is no type erasure. The generic types can be extracted from the reflection data (unfortunately there's no easy built-in way, but have a look here: http://www.artima.com/weblogs/viewpost.jsp?thread=208860).
I've read the whole SCJP6 book Sierra and Bates book, scored 88% the exam.
But still, i never heard of how this kind of code works as it's not explained in the generics chapter:
Collections.<TimeUnit>reverseOrder()
What is this kind of generics usage?
I discovered it in some code but never read anything about it.
It seems to me it permits to give some help to type inference.
I've tried to search about that but it's not so easy to find (and it's not even in the SCJP book/exam!)
So can someone give me a proper explaination of how it works, which are all the usecases etc?
Thanks
Edit
Thanks for the answers but i expected more details :) so if someone want to add some extra informations:
What about more complex cases like
Using a type declared in class , can i do something like Collections.<T>reverseOrder() for exemple?
Using extends, super?
Using ?
Giving the compiler only partial help (ie O.manyTypesMethod<?,MyHelpTypeNotInfered,?,?,?,?,?>() )
It is explicit type specification of a generic method. You can always do it, but in most cases it's not needed. However, it is required in some cases if the compiler is unable to infer generic type on its own.
See an example towards the end of the tutorial page.
Update: only the first of your examples is valid. The explicit type argument must be, well, explicit, so no wildcards, extends or super is allowed there. Moreover, either you specify each type argument explicitly or none of them; i.e. the number of explicit type arguments must match the number of type parameters of the called method. A type parameter such as T is allowed if it is well defined in the current scope, e.g. as a type parameter of the enclosing class.
You are 100% correct, it is to help with type inference. Most of the time you don't need to do this in Java, as it can infer the type (even from the left hand side of an assignment, which is quite cool). This syntax is covered in the generics tutorial on the Java website.
Just a small addition to the other responses.
When getting the according compiler error:
While the "traditional" casting approach
(Comparator<TimeUnit>) Collections.reverseOrder()
looks similar to the generics approach
Collections.<TimeUnit>reverseOrder()
the casting approach is of course not type-safe (possible runtime exception), while the generics approach would create a compilation error, if there is an issue. Thus the generics approach is preferred, of course.
As the other answers have clarified, it's to help the compiler figure out what generic type you want. It's usually needed when using the Collections utility methods that return something of a generic type and do not receive parameters.
For example, consider the Collections.empty* methods, which return an empty collection. If you have a method that expects a Map<String, String>:
public static void foo(Map<String, String> map) { }
You cannot directly pass Collections.emptyMap() to it. The compiler will complain even if it knows that it expects a Map<String, String>:
// This won't compile.
foo(Collections.emptyMap());
You have to explicitly declare the type you want in the call, which i think looks quite ugly:
foo(Collections.<String, String>emptyMap());
Or you can omit that type declaration in the method call if you assign the emptyMap return value to a variable before passing it to the function, which i think is quite ridiculous, because it seems unnecessary and it shows that the compiler is really inconsistent: it sometimes does type inference on generic methods with no parameters, but sometimes it doesn't:
Map<String, String> map = Collections.emptyMap();
foo(map);
It may not seem like a very important thing, but when the generic types start getting more complex (e.g. Map<String, List<SomeOtherGenericType<Blah>>>) one kind of starts wishing that Java would have more intelligent type inference (but, as it doesn't, one will probably start writing new classes where it's not needed, just to avoid all those ugly <> =D).
In this case it is a way of telling the reverseOrder method what kind of ordering should be imposed on the object, based on what type you specify. The comparator needs to get specific information about how to order things.
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())
There have been a couple of times when I've felt the need to do something like the following:
private <T extends Type> Map<GenericClass1<T>,GenericClass2<T>> map;
...or something to that effect. Essentially, using an identical bound in the two arguments for map. (This isn't an actual example, just shows the idea.)
I know this (unfortunately) isn't possible and that it's only available on class definitions and method signatures. My question however is why isn't it available on fields? Is it purely a design choice or is there some technical reason behind it that I'm missing? I've had a think and can't see why this shouldn't be possible from a technical perspective, as far as I can see everything is there for the compiler to work it out correctly and none of the generic information is required at runtime.
<T> means ONE class, not A class.
When your object is instanced T is bound to this ONE class.
You are trying to put two objects with diffrent interfaces (used diffrently because they take/return diffrent types) in to the same container. This is a error because when you take them out of the container (the map) you dont know what it was you put in.
Hope this is the answer you were looking for.
Edit: That said you can have a container that holds members based on there class, to automatically create a new map for EACH type of T. You would then need to know what T was in order to access it.
In general, if you dont want the type information anymore, throw it away. If you do then putting it in the same container as something of another type will throw it away anyway for all practical reasons.
Let's assume that you want to instantiate your map variable. Theoretically you will have to write something like this:
map = new HashMap<GenericClass1<String>,GenericClass2<String>>();
Ok. But now what does not make sense to me anymore is what arguments the put or get methods will accept/return? T? Uh... what is T? GenericClass1|2<String>? Again makes no sense, does it? After all I see no String in the map declaration. So I guess there is no really correct instantiation and usage of this generic variable.
Cheers!
Oh, how I have longed for something like:
private <T> Map<Class<T>, T> instanceCache;
public <T> T getInstanceOf(Class<T> clazz) {
return instanceCache.get(clazz);
}
But as you mentioned, it's completely impossible in Java. The method declaration above is fine, but there's no way to declare the variable such that there's not a cast in the method. Simply add this to the growing list of things you hate about generics and move on.
You can use ? as following:
private List<? extends List> l = new ArrayList<List>();
I hope this helps.
I've occasionally heard that with generics, Java didn't get it right. (nearest reference, here)
Pardon my inexperience, but what would have made them better?
Bad:
Type information is lost at compile time, so at execution time you can't tell what type it's "meant" to be
Can't be used for value types (this is a biggie - in .NET a List<byte> really is backed by a byte[] for example, and no boxing is required)
Syntax for calling generic methods sucks (IMO)
Syntax for constraints can get confusing
Wildcarding is generally confusing
Various restrictions due to the above - casting etc
Good:
Wildcarding allows covariance/contravariance to be specified at calling side, which is very neat in many situations
It's better than nothing!
The biggest problem is that Java generics are a compile-time only thing, and you can subvert it at run-time. C# is praised because it does more run-time checking. There is some really good discussion in this post, and it links to other discussions.
The main problem is that Java doesn't actually have generics at runtime. It's a compile time feature.
When you create a generic class in Java they use a method called "Type Erasure" to actually remove all of the generic types from the class and essentially replace them with Object. The mile high version of generics is that the compiler simply inserts casts to the specified generic type whenever it appears in the method body.
This has a lot of downsides. One of the biggest, IMHO, is that you can't use reflection to inspect a generic type. Types are not actually generic in the byte code and hence can't be inspected as generics.
Great overview of the differences here: http://www.jprl.com/Blog/archive/development/2007/Aug-31.html
Runtime implementation (ie not type erasure);
The ability to use primitive types (this is related to (1));
While the wildcarding is useful the syntax and knowing when to use it is something that stumps a lot of people. and
No performance improvement (because of (1); Java generics are syntactic sugar for castingi Objects).
(1) leads to some very strange behaviour. The best example I can think of is. Assume:
public class MyClass<T> {
T getStuff() { ... }
List<String> getOtherStuff() { ... }
}
then declare two variables:
MyClass<T> m1 = ...
MyClass m2 = ...
Now call getOtherStuff():
List<String> list1 = m1.getOtherStuff();
List<String> list2 = m2.getOtherStuff();
The second has its generic type argument stripped off by the compiler because it is a raw type (meaning the parameterized type isn't supplied) even though it has nothing to do with the parameterized type.
I'll also mention my favourite declaration from the JDK:
public class Enum<T extends Enum<T>>
Apart from wildcarding (which is a mixed bag) I just think the .Net generics are better.
I'm going to throw out a really controversial opinion. Generics complicate the language and complicate the code. For example, let's say that I have a map that maps a string to a list of strings. In the old days, I could declare this simply as
Map someMap;
Now, I have to declare it as
Map<String, List<String>> someMap;
And every time I pass it into some method, I have to repeat that big long declaration all over again. In my opinion, all that extra typing distracts the developer and takes him out of "the zone". Also, when code is filled with lots of cruft, sometimes it's hard to come back to it later and quickly sift through all the cruft to find the important logic.
Java already has a bad reputation for being one of the most verbose languages in common use, and generics just add to that problem.
And what do you really buy for all that extra verbosity? How many times have you really had problems where someone put an Integer into a collection that's supposed to hold Strings, or where someone tried to pull a String out of a collection of Integers? In my 10 years of experience working at building commercial Java applications, this has just never been a big source of errors. So, I'm not really sure what you're getting for the extra verbosity. It really just strikes me as extra bureaucratic baggage.
Now I'm going to get really controversial. What I see as the biggest problem with collections in Java 1.4 is the necessity to typecast everywhere. I view those typecasts as extra, verbose cruft that have many of the same problems as generics. So, for example, I can't just do
List someList = someMap.get("some key");
I have to do
List someList = (List) someMap.get("some key");
The reason, of course, is that get() returns an Object which is a supertype of List. So the assignment can't be made without a typecast. Again, think about how much that rule really buys you. From my experience, not much.
I think Java would have been way better off if 1) it had not added generics but 2) instead had allowed implicit casting from a supertype to a subtype. Let incorrect casts be caught at runtime. Then I could have had the simplicity of defining
Map someMap;
and later doing
List someList = someMap.get("some key");
all the cruft would be gone, and I really don't think I'd be introducing a big new source of bugs into my code.
Another side effect of them being compile-time and not run time is that you can't call the constructor of the generic type. So you can't use them to implement a generic factory...
public class MyClass {
public T getStuff() {
return new T();
}
}
--jeffk++
Ignoring the whole type erasure mess, generics as specified just don't work.
This compiles:
List<Integer> x = Collections.emptyList();
But this is a syntax error:
foo(Collections.emptyList());
Where foo is defined as:
void foo(List<Integer> x) { /* method body not important */ }
So whether an expression type checks depends on whether it is being assigned to a local variable or an actual parameter of a method call. How crazy is that?
Java generics are checked for correctness at compile time and then all type information is removed (the process is called type erasure. Thus, generic List<Integer> will be reduced to its raw type, non-generic List, which can contain objects of arbitrary class.
This results in being able to insert arbitrary objects to the list at runtime, as well as it's now impossible to tell what types were used as generic parameters. The latter in turn results in
ArrayList<Integer> li = new ArrayList<Integer>();
ArrayList<Float> lf = new ArrayList<Float>();
if(li.getClass() == lf.getClass()) // evaluates to true
System.out.println("Equal");
Java generics are compile-time only and are compiled into non-generic code. In C#, the actual compiled MSIL is generic. This has huge implications for performance because Java still casts during runtime. See here for more.
The introduction of generics into Java was a difficult task because the architects were trying to balance functionality, ease of use, and backward compatibility with legacy code. Quite expectedly, compromises had to be made.
There are some who also feel that Java's implementation of generics increased the complexity of the language to an unacceptable level (see Ken Arnold's "Generics Considered Harmful"). Angelika Langer's Generics FAQs gives a pretty good idea as to how complicated things can become.
I wish this was a wiki so I could add to other people... but...
Problems:
Type Erasure (no runtime availability)
No support for primative types
Incompatability with Annotations (they were both added in 1.5 I'm still not sure why annotations don't allow generics aside from rushing the features)
Incompatability with Arrays. (Sometimes I really want to do somthing like Class<? extends MyObject>[], but I'm not allowed)
Wierd wildcard syntax and behavior
The fact that generic support is inconsistant across Java classes. They added it to most of the collections methods, but every once in a while, you run into an instance where its not there.
Java doesn't enforce Generics at run time, only at compile time.
This means that you can do interesting things like adding the wrong types to generic Collections.
If you listen to Java Posse #279 - Interview with Joe Darcy and Alex Buckley, they talk about this issue. That also links to a Neal Gafter blog post titled Reified Generics for Java that says:
Many people are unsatisfied with the
restrictions caused by the way
generics are implemented in Java.
Specifically, they are unhappy that
generic type parameters are not
reified: they are not available at
runtime. Generics are implemented
using erasure, in which generic type
parameters are simply removed at
runtime.
That blog post, references an older entry, Puzzling Through Erasure: answer section, that stressed the point about migration compatibility in the requirements.
The goal was to provide backwards
compatibility of both source and
object code, and also migration
compatibility.