I am trying to use a generic method for the first time and am somewhat confused. I created a simple example to demonstrate that I am probably going about this the wrong way and need straightening out. I am using Eclipse 3.6.1. I was under the impression that the compiler determined the argument types through inference, but am not sure why it is forcing me use casting in the generic method. This is a simple example.
class Test1
{
Test1 () {};
public String getX () {return "Test1"};
};
class Test2
{
Test2 () {};
public String getX () {return "Test2"};
};
my main method:
public static void main(String args[])
{
Test1 tst1 = new Test1();
Test2 tst2 = new Test2();
System.out.println("result: " + displayTest(tst1, tst2));
}
static <T,S> boolean displayTest(T x, S y)
{
System.out.println("X: " + ((Test1) x).getX());
System.out.println("Y: " + ((Test2) y).getX());
if (((Test1) x).getX().equals(((Test2) y).getX()))
return true;
else
return false;
}
I thought the compiler would know that T in this case was an instance of Test1 and S was Test2, yet in Eclipse, getX is not a valid method. In order to get this to compile, it forces me to cast the objects to the correct type, which seems to me to be against the general principles of a generic method.
Obviously, I am not getting this and am doing something wrong. How does the compiler know what the types are in the generic method then ? How should something like this be done ? In my large system where I am trying to implement this, I have several methods that operate on different types of objects and am trying to make them generic. i.e. Method 1 calls Method 2 (which uses the generic type), which in turn calls Method 3 (again passing the generic types). I was hoping only the start of the function calls (calling of method 1 in this case) needed to know what type the objects were and all subsequent methods were just generic methods.
Many thanks.
The compiler does not know the types inside the method. They can be anything.
If you use <T extends Y>, where Y is an interface defining getY(), it would work.
The point of generics is to ensure compile-time safety. They are mainly a compile-time notion. For example:
public static <T> T instantiate(Class<T> clazz) throws Exception {
return clazz.newInstance();
}
This method can be used, without any cases, like this:
Foo foo = instantiate(Foo.class);
Bar bar = instantiate(Bar.class);
Another example, from the Collections framework. There is Collections.enumeration(collection), which is generic. So:
Enumeration<String> enumeration1 =
Collections.enumeration(new ArrayList<String>(..));
Enumeration<Integer> enumeration2 =
Collections.enumeration(new ArrayList<Integer>(..));
No casts, but you are certain that these will be the types. And then the nextElement() method will return either String or Integer, without the need to cast:
String s = enumeration1.nextElement();
Integer i = enumeration2.nextElement();
Without generics, you would need to use casts in these examples, and if you have passed the wrong argument, you would get a runtime exception (most likely a ClassCastException). With generics you get the exception at compile-time.
The compiler actually adds these casts on your behalf, but only after it is certain that the cast can't go wrong.
The problem is that at compile time the compiler does not know the concrete type of T and S. Your main method calls displayTest with concrete instances of Test1 and Test2, but there's nothing stopping you from calling it with, say, a String and an Integer.
This means that you can't call getX on x because you can't guarantee that T is a subclass of Test1.
You can constrain the type of T and S with captures:
boolean <T extends Test1, S extends Test2> displayTest(T x, S y)
This tells the compiler that T must be a Test1 (or a subclass of Test1) which means you don't need to cast.
Why would the compiler know that T and S are Test1 and Test2? You can call displayTest from anywhere in your code, any number of times with any number of different object types. What is it supposed to do there?
Also, eclipse != compiler. Eclipse's code completion doesn't "compile" anything. The compiler knows exactly what types are being put in there, but only for each CALL to that function. That's why it's called generic. It can take any generic type.
When you say the compiler determines the argument types through inference, it means something different than what you are thinking.
Since displayMethod is a generic method, the "proper" way to call the method is to tell the compiler what T and S stand for in this particular method call by calling it as
ClassName.<Test1,Test2>displayMethod(tst1,tst2)
But since java is smart, the compiler can infer w/o you telling it exactly what T and S, that T and S are going to be Test1 and Test2 if you just do,
ClassName.displayMethod(tst1, tst2)
Also, like cameron skinner said, you can fix your code by making T extend Test1 and S extend Test2. Another fix that you can do is leave <T, S> as it is, and renaming your getX() method to the override the toString() method. Since toString() is a member of the Object class, the generic classes T and S will have access to it and you won't need to restrict your method to only accept arguments which are of type Test1 and Test2
Related
I have a function that should take an instance of anything extending Object, and just pass the casted instance to a function. I don't want to use a switch, as the function can accept a huge number of object types, so it would become a very big method.
public void attachBufferData(ContextConstant bufferType, Object<T> data, ContextConstant usage) {
glBufferData(bufferType.getGLType(), (T) data, usage.getGLType());
}
The code above doesn't work (as Object isn't a generic type), but it should get across what I'm trying to do.
----- EDIT -----
Ok, I tried this:
public void attachBufferData(ContextConstant bufferType, Object data, Class<?> dataType, ContextConstant usage) {
glBufferData(bufferType.getGLType(), dataType.cast(data), usage.getGLType());
}
But I get a compile error glBufferData(int, long, int) is not applicable for arguments (int, capture#1-of ?, int). I guess it's a massive switch statement then :(
You can't do it like this, I'm afraid. There are three things to consider. I think (2) is the one you really want, but I'm not absolutely certain, so I've left all three issues in there for you to think about.
What signature does glBufferData() have (if it's not overloaded)? If its second parameter is of type Object, then whatever you pass will end up being viewed as an Object there, even if it's a subclass, so you wouldn't achieve anything by having it cast. You might as well just have the type of data as the same type as the second parameter to glBufferData().
If glBufferData() is an overloaded method, and you want to be calling the right one, then you can't do it dynamically: you need some code to test the real type of the class at runtime, and then you choose the right version to call. Choice of overloaded method gets resolved at compile time, not runtime, so you can't just pass it a specific instance you don't know about at compile time and then have it select the right version.
If glBufferData() is a non-overloaded method you've written, contained within your class, then you do have another and better option, which is to make your class generic. If your class takes a type parameter of T, then you can have T data as the second parameter of attachBufferData(), and you can have T data as the second parameter of glBufferData(), so that the types match up.
The point about method overloading is that it's not nearly as clever as it looks. As far as the compiler is concerned, there's really no difference between these two cases.
Case 1:
public void f(int x);
public void f(String s);
Case 2:
public void f(int x);
public void g(String s);
Although we think of case 1 as having just one overloaded method, and case 2 as having two separate methods, as far as the compiler's concerned, in each case there are two distinct methods, and they're distinct because they have distinct signatures (ignoring return type). In both cases, the compiler can choose the right method to call based on the code you write, because it can look at the type of the arguments and the name of the method you've asked for, and find one that matches. The fact that two have the same name is of no more significance than having two methods with different names but the same parameter types.
There's no mechanism for choosing which method to call at runtime in case 1, any more than there is in case 2.
You can declare the type at class level and reuse it wherever required, below is an example.
public class ExceptionHolder<T>
{
private List<T> errors = new ArrayList<T>();
public void addError( T t )
{
errors.add( t );
}
public void addError( List<T> t )
{
errors.addAll( t );
}
}
In the calling code
ExceptionHolder<String> exHolder = new ExceptionHolder<String>();
String can be substitued for any object as needed.
How can I create a list with a specified type argument?
For example:
LinkedList<Integer> list = createList(LinkedList.class, Integer.class);
I've tried creating a method for it, but the method doesn't include the type argument when creating the new instance.
public static <T, L extends List<T>> L createList(Class<L> listClazz, Class<T> valueClazz) throws Exception
{
return listClazz.getConstructor().newInstance();
//Instead of
// new L<T>();
//does
// new L();
}
I hope my question is clear enough, thank you for any help.
You can't do that. Generics have no business at runtime, so you can't create parameterized instance at runtime using reflection. At runtime, LinkedList<Intege> is just a LinkedList. The type information - Integer is erased at compile time through "type erasure". The point is, why would you like to do that?
//Instead of
// new L<T>();
//does
// new L();
The two "do" exactly the same thing. How do you know it "does" one and not the other? Type parameters are compile-time illusions for type-checking. They are not relevant in what the code "does". To create an instance of LinkedList<T>, you do not need to know what T is, because it doesn't really exist after type erasure.
I think the issue you are having has nothing to do with the code inside the function, but with the signature of the method. By the very fact that the method is declared as it is, and you are passing an expression of type Class<LinkedList> (the type of LinkedList.class) to it, L must be LinkedList, and since the method returns L, the compiler must consider its return to by type LinkedList. None of this has anything to do with the code inside the method.
If you want the method's return type to be considered as LinkedList<Integer>, then just pass in an expression of type Class<LinkedList<Integer>>. Simple, right? How do you get an expression of type Class<LinkedList<Integer>>? For one, you could do (Class<LinkedList<Integer>>)(Class<?>)LinkedList.class.
If that seems kind of bogus, it's because Class is fundamentally a runtime thing -- it allows runtime creation of an object from that class. Using it with Generics, a compile-time type-checking mechanism, is fundamentally flawed. There is just one class object in existence for any class, e.g. LinkedList. Is it LinkedList or LinkedList<Integer> or LinkedList<String>? Well, it could be all of these, in the sense the the newInstance() method could create any of them (there is no difference at runtime).
Methods that are generic using the T parameter can for sure be handy. However, I am curious what the use of a generic method would be if you pass an argument such as Class<T> clazz to the method. I've come up with a case that maybe could be an possible use. Perhaps you only want to run a part of the method based on the type of class. For example:
/** load(File, Collection<T>, Class<T>)
* Creates an object T from an xml. It also prints the contents of the collection if T is a House object.
* #return T
* Throws Exception
*/
private static <T> T void load(File xml, Collection<T> t, Class<T> clazz) throws Exception{
T type = (T) Jaxb.unmarshalFile(xml.getAbsolutePath(), clazz); // This method accepts a class argument. Is there an alternative to passing the class here without "clazz"? How can I put "T" in replace of "clazz" here?
if (clazz == House.class) {
System.out.println(t.toString());
} else {
t.clear();
}
return T;
}
Is this an accepted practice? When is the Class<T> clazz argument useful with generic methods?
Is this an accepted practice?
Well, to me.. no not really. To me, it seems somewhat pointless when you can simply define some boundaries on the type of T. For example:
private static <T extends House> void load(Collection<T> t)
This will guarantee that either the object is of type House or of a subclass of House, but then again if you only want an instance of type House or it's subclasses, it should really just be:
private static void load(Collection<House> houses)
The idea of generics is to make a method or a class more malleable and extensible, so to me it seems counter-intuitive to start comparing class types in the method body, when the very notion of generics is to abstract away from such details.
I'd only pass class objects if the generic type could not be derived otherwise. In your case, the compiler should be able to infer T from the collection. To treat specific objects differently, I'd use polymorphism - e.g. House#something() and Other#something(), and just call anyObject.something().
I think it is acceptable but if it can be avoided then you should. Typically, if you can have different methods which accepts different type, then do it instead of one method which uses if clauses to do something different depending on the type of the parameter. You could also delegates to the class the operation you want to make specific for a given type.
In your case, you could simply test the type of each element of the collection using instanceof, to do what you need for the specific type. But it won't work if the list is empty.
A typical use is if you need to get the type to create it and you can find it from another way. For instance, Spring uses it to load a bean from its name:
<T> T getBean(Class<T> requiredType)
In that case, it cannot be avoided (without having to cast).
If the returned value or other parameters types are dependent or need to be equal, generics will add compile time checks, so that there's no need to cast to T.
Examples
<T> T createNewInstanceOfType(Class<T> type);
<T> void addValueToCollection(Collection<T> collection,T value);
<T> List<Class<? extends T>> findSubClassesInClasspath(Class<T> superType);
Raw types
It is still possible to defer a casting error until runtime (ClassCastException) with some casts, e.g. with implicit casts from non-generic (raw) types to generic ones:
List nonGenericList = new ArrayList();
nonGenericList.add(new Integer(42));
List<String> wreckedList = nonGenericList;
The compiler will generate a bunch of warnings, unless you suppress them with annotations or compiler settings.
Compiler Settings (Eclipse):
For example, the usage of raw types generates a warning per default, one can treat warnings as errors and even as fatal errors:
You would pass a Class<T> argument in generics if, and only if, you would pass a Class argument before generics. In other words, only if the Class object is used in some way. Generics serves as a compile-time type checking tool. However, what arguments you pass should be determined by the runtime logic of the program, and should be irrelevant of generics.
I haven't seen passing a Class object in order to check the runtime type of an object as a common use case for generics. If you're doing that, there's a good chance that there's a better way to set up your class structure.
What I have seen is if you need to create a new instance of the class in question, or otherwise use reflection. In that case you do have to pass the Class object, because Java cannot derive it at runtime thanks to type erasure.
In your case actually having the Generic parameter is not strictly needed.
Since the output of the function you are describing does not depend on the type of the input you might as well use wild cards.
private static void stuff(Collection<?> t){
Object next = t.iterator().next(); //this is ugly and inefficient though
if(next instanceof House){
System.out.print(next.toString());
}else{
t.clear();
}
}
The only time you should use generic parameter is when the type of the result of a function will be dependent of the type of the parameters.
You will need to pass the Class corresponding to the type when your code will need it; most of the time this happens when:
- You need to cast/type check objects to T
- There is serialization/deserialization involved.
- You cannot access any instance of T in your function and you cannot call the getClass() method when you need it.
Passing a Class on every generic function will result in you passing an unnecessary parameter most of the time, which is regarded as bad practice.
I answered a similar discussion in the past:
When to use generic methods and when to use wild-card?
I am still experimenting with how Java handles generics. I stumbled upon the fact/issue/thing that if you have a generic interface like A<T>, you cannot really check afterwards if some object is actually implementing A<B> or A<C>.
I wondered if that could cause actual problems.
Now I have tried this code:
static interface A<T> { void foo(T obj); }
static class B implements A<B> {
public void foo(B obj) { obj.bar(); }
void bar() {}
}
static {
assert (new B() instanceof A<?>);
((A<?>) new B()).foo(new Object());
}
This gives me this error (for the foo-call):
The method foo(capture#1-of ?) in the type Main.A<capture#1-of ?> is not applicable for the arguments (Object)
I wonder why that is. Eclipse tells me that the signature of foo after the cast to A<?> is foo(? obj) which I thought is the same as foo(Object obj).
The assert succeeds.
What I tried to figure out is at what point exactly does it cast the object when I call the foo function.
Also, how can I call foo from A<?>? This is the thing I actually need to be able to do. Or is that impossible with any other parameter than null?
A more real-world example where I actually wonder about this: I use the Comparable<T> interface a lot. That case is actually even more complicated; I might open another question about that if this here doesn't answer it.
I wonder why that is. Eclipse tells me that the signature of foo after the cast to A is foo(? obj) which I thought is the same as foo(Object obj).
Nope, absolutely not. Imagine A<T> is List<T> with foo(T) being add(T) and so A<?> is List<?>. Should you be able to do this?
List<String> strList = new ArrayList<String>();
List<?> wcList = strList;
wcList.add(Integer.valueOf(6)); //possible if add(?) is same as add(Object)
//...
String str = strList.get(0);
Of course not, since you'd get a ClassCastException in the final line.
What foo(?) really means is that the method applies to some unknown but specific type. You can't typically invoke these methods unless you pass null as the parameter, which is acceptable to assign to any reference type.
If you have "foo(? obj)" then the ? could be any type. If it is say String then you can't pass, say, an Integer to it. All you can pass is null.
Casting and use of instanceof should normally be avoided unless unavoidable (such as implementing equals), particularly with generics.
The compiler is correct because it does a compile-cast test on compile-time.
((A<?>) new B()).foo(new Object());
is errornous because the compiler expected
((A<?>) new B()).foo(A object)....
meaning that it wanted anything of type A or its children. Object is the parent of A and it doesn't match the compile-test Parameter type for compilation.
When you check if an instance of B is an instance of A<?> you just do the same thing as new B() instanceof A. You don't really check how T is set, it's just "something".
Later in the code with the cast, you tell that you'll use B as a A<?> so the variable will have the characteristic of a A but the generic type is still "something". This "something" exists and is probably a specified class but your don't care of the exact type.
That's why when you use the foo() method which take a T in parameter, you can't pass a "something" in parameter, because you don't know what it is, it could be an Object but it could be anything else.
Because of this the compiler tells you foo(capture#1-of ?) isn't applicable for the argument Object. The needed parameter is "something" but not necessarily an Object.
Then, when would you need this feature ?
For example if you work with a map, if you don't really care of the type of the key (if you only work with the values() method for example), you could do something like this :
Map<?, V> m
This way you won't be able to use features related to the key of the map (but you don't care about that), but you'll be able to use a map with any kind of key.
No, foo(? obj) is not actually the same as foo(Object obj). The difference is, when the parameter type is Object, it's explicitly stating that any type of object is legal. With a parameter type of ?, the method states that it does not know what type of object is legal... therefore nothing is legal except for null.
The reasoning for this becomes apparent when you consider a List rather than an arbitrary interface. Look at this method:
public void foo(List<?> list) {
list.add(...); // what can we add here?
}
The ? indicates that any type of List is acceptable... the List passed in could be a List<String>, a List<Integer> or a List<Map<Foo, Bar>>. There's no way of knowing. Note that this is only a problem with methods that consume generic parameters, such as add or your foo method. For methods that produce (return) an object of the generic type, it's fine to use such a method and assign the result as an Object. This, unlike the consumer method, cannot corrupt the internal state of the generic object.
Also notice that when you call ((A<?>) new B()).foo(new Object()), you're trying to do something illegal... if something (such as the Object) that is not a B were to be passed to B's foo method, it would explode at runtime. The compiler is correctly preventing you from doing this.
You may also want to check out my answer to another question here which explains some things about bounded wildcard types and such.
This is a simple explanation:
A<?> means A of unknown parametrized type. Given
A<?> ref = new B()
ref could point to any A: A<Object, A<String>, anything. So you can not call A.foo(new Object()) because in case of A<?> reference there is no way to know which parameter ref.foo() accepts. Actually the only thing valid is ref.foo(null).
Many have clarified that point about ? but noone has really answered the main question yet, so here it is:
It is possible to call A#foo like this:
((A) new B()).foo(new Object());
The cast then is done inside of foo.
I saw a java function that looked something like this-
public static<T> foo() {...}
I know what generics are but can someone explain the in this context? Who decides what T is equal to? Whats going on here?
EDIT: Can someone please show me an example of a function like this.
You've missed the return type out, but apart from that it's a generic method. As with generic types, T stands in for any reference type (within bounds if given).
For methods, generic parameters are typically inferred by the compiler. In certain situations you might want to specify the generic arguments yourself, using a slightly peculiar syntax:
List<String> strings = Collections.<String>emptyList();
In this case, the compiler could have inferred the type, but it's not always obvious whether the compiler can or can't. Note, the <> is after the dot. For syntactical reasons the type name or target object must always be specified.
It's possible to have generic constructors, but I've never seen one in the wild and the syntax gets worse.
I believe C++ and C# syntaxes place the generic types after the method/function name.
The context is a generic method as opposed to a class. The variable <T> applies only to the call of the method.. The Collections class has a number of these; the class itself is not generic, but many of the methods are.
The compiler decides what T is equal to -- it equals whatever gets the types to work. Sometimes this is easier then others.
For example, the method static <T> Set<T> Collections.singleton(T o) the type is defined in the parameter:
Collections.singleton(String T)
will return a Set<String>.
Sometimes the type is hard to define. For example sometimes there is not easily enough information to type Collection.emptyList(). In that case you can specify the type directly: Collection.<String>emptyList().
T it's the formal type parameter wich will be replaced by the actual type
argument used at the instantiation of the object.
For example, here is the List and Iterator definitios in package java.util:
public interface List<E>{
void add(E x);
Iterator<E> iterator();
}
public interface Iterator<E>{
E next();
boolean hasNext();
}
Then you can instantiate a List this way:
List<String> ls = new ArrayList<String>()
Where you might imagine that List stands for a version of List where E has
been uniformly replaced by String:
public interface StringList{
void add(String x)
Iterator<String> iterator();
}