Studying Java, I've come across generic methods.
public <T> void foo(T variable) { }
That is, a method which takes a parameter with an undecided type (รก la PHP?). I'm however unable to see how this would be a good solution - especially since I've come to fall in love with a strongly typed languages after coming from a loose ones.
Is there any reason to use generic methods? If so, when?
Those who are coming from prior to Java 5 background knows that how inconvenient it was to store object in Collection and then cast it back to correct Type before using it. Generics prevents from those. it provides compile time type-safety and ensures that you only insert correct Type in collection and avoids ClassCastException in runtime.
So it provides compile time type-safety and casting. When you want to write complex APIs with complex method signatures it will save you a lot both when writing the API and when using the API and prevents writing lots of code for casting part and catch your errors at compile time. just take a look at java.util.Collection package and see the source code.
As a developer I always want compiler to catch my error at compile time and inform me when I want to compile it then i will fix my errors and at runtime there won't be many errors related to type-safety.
for more info see :
http://javarevisited.blogspot.com/2011/09/generics-java-example-tutorial.html
http://javarevisited.blogspot.com/2012/06/10-interview-questions-on-java-generics.html
Generics, among other things, give you a way to provide a template -- i.e. you want to do the same thing, and the only difference is the type.
For example, look at the List API, you will see the methods
add(E e)
For every list of the same type you declare, the only thing different about the add method is the type of the thing going into the list. This is a prime example of where generics are useful. (Before generics were introduced to Java, you would declare a list, and you could add anything to the list, but you would have to cast the object when you retrieved it)
More specifically, you might want 2 ArrayList instances, one that takes type1 and one that takes type2. The list code for add is going to do the same thing, execute the same code, for each list (since the two lists are both ArrayList instances), right? So the only thing different is what's in the lists.
(As #michael points out, add isn't a true example of a generic method, but there are true generic methods in the API linked, and the concept is the same)
There's nothing non-strongly typed about generic functions in general. The type is resolved and checked at compile time. It's not an undecided type, it's one of a range of possible types (these can be constrained, in your example they are not). At compile time it is known and decided.
As hvgotcodes says, the Collections API contains a number of good examples of this in use.
The main objective of Generic concepts are :
To provide type safety to the Collections so that they can hold only
one particular type of object.
To resolve typecasting problems.
To hold only String type of object a Generic version of ArrayList can be declare as follows :
ArrayList l = new ArrayList ();
To know more : http://algovalley.com/java/generics.php
Related
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.
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 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.
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.
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.