public class Foo<T extends Bar>{
private Class<T> _type;
public Foo( Class<T> _type ){
this._type = _type;
}
public Collection<T> hypothetical( List<T> items ){ //PROBLEMATIC
return dostuffWithItems( items );
}
}
Usage:
Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
List<ChildBar> items = ( List<ChildBar> ) foo.hypothetical( new ArrayList<ChildBar>() ); //COMPILER ERROR: The method hypothetical(List<capture#2-of ?>) in the type Foo<capture#2-of ?> is not applicable for the arguments (List<ChildBar>)
The compiler would either accept
casting List<ChildBar> items argument to List<?>
or changing the hypothetical( List<T> items ) signature to either
a) hypothetical( List<ChildBar> items ) or
b) hypothetical( List<? extends Bar> items )
However, none of the alternatives assure that the hypothetical method's List items argument T type is the equivalent runtime type of the Foo class T parametric type. I am currently using an extra method to verify the parametric types at the moment.
Is there a better way within Java generics constructs to achieve this automatically without the extra logic? Or better yet, why can I not declare foo as Foo<? extends Bar> and then fill in the actual type parameter at runtime?
I edited your code and added the missing stuff to make it compilable, and I can confirm that the only problematic parts are:
The missing dostuffWithItems method.
The typos with the hypothetical method name.
Assigning a Collection<ChildBar> to a List<ChildBar>.
The first two are easy to fix.
The last one requires you to either change the change the API method, or change the code where you are calling it. Neither of these is (IMO) problematic. Furthermore, the
It is worth noting that you would get all of these errors if the types were non-generic. You can't assign a Collection to a List without a typecast.
Here's my code for you to play with. (Copy and paste into appropriately named files ...)
public class Bar {
}
public class ChildBar extends Bar {
}
import java.util.*;
public class Foo<T extends Bar> {
private Class<T> _type;
public Foo( Class<T> _type ) {
this._type = _type;
}
public Collection<T> hypothetical( List<T> items ) {
return items; // dummy implementation ...
}
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
Collection<ChildBar> items =
foo.hypothetical( new ArrayList<ChildBar>() );
}
}
The accepted answer doesn't precisely explain why the snippet in the question (after edits) is rejected by the compiler.
We start from the observation that the snippet from #Stephen C's answer is accepted, while revision 8 of the question is rejected. The difference is: in the latter version the variable foo is declared with a wildcard-parameterized type Foo<? extends ChildBar>, while Stephen C had copied Foo<ChildBar> from an earlier revision (we all seem to agree that this is a suitable way to resolve the compile error).
To understand why this difference is crucial please see that with Foo<? extends ChildBar> foo this wildcard propagates as a capture into the signature for the invocation of foo.hypothetical, so this invocation is rendered as hypothetical(List<capture#2-of ?>), meaning that the parameter has an unknown (upper-bounded) type parameter. List<ChildBar> is not compatible to that type, hence the compile error.
Also note that all mentions of "runtime" in this thread are inappropriate, all this is statically resolved at compile time. Perhaps you meant invocation type, or type of the actual argument, as opposed to the declared type (of the formal parameter). The actual runtime type is unknown to the compiler.
This seems to be currently impossible in Java.
Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
This leave foo with an ambiguous parametric type. It is obvious that ChildBar would become the true de facto parametric type. The call to the foo.hypothetical() method with the List<ChildBar> exposes this assumption to be untrue. Although foo.hypothetical only accepts a List<> argument containing elements of the foo parametric type, it still fails to recognize that the argument was a list of ChildBar objects.
For this use case, the object parametric type must be specified during foo declaration in order make it a part and parcel of the foo runtime reference.
Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
All conforming List<ChildBar> arguments of the foo.hypothetical method will now correctly be accepted as carrying elements of the foo's declared parametric type.
Related
Suppose I have two methods:
public Set<String> method1()
public List<String> method2()
How do I make a generic method off this? Specifically, I'm looking to genericize the "Set" and "List".
Here's an attempt that didn't work:
public static <T extends Collection> T<String> genericMethod
It's showing a compiler error: Type "T" does not have type parameters.
As far as the signature goes, it would be
public static <T extends Collection<String>> T genericMethod() {
...
}
Presumably, genericMethod is going to create an instance of T at some point and return that, rather than just returning null (that wouldn't be very useful, would it?), but there is no guarantee that T has any constructors at all. And due to type erasure, the runtime wouldn't know what type to create anyway. To work around this, the method would also need to accept a parameter that tells it how to create a T:
public static <T extends Collection<String>> T genericMethod(Supplier<? extends T> tSupplier) {
...
}
Now, rather than saying new T(), which is invalid, you do tSupplier.get() to get a T.
If the caller wants a Set<String>, for example, they would do:
Set<String> set = genericMethod(HashSet::new);
Note that the specific implementation of the collection is now specified by the caller, rather than hidden as an implementation detail of genericMethod. This is inevitable, as the specific type of collection (T) is now unknown to genericMethod.
Background: the question came up in this answer (the first revision of the answer, to be exact). The code presented in this question is reduced to the bare minimum to explain the problem.
Suppose we have the following code:
public class Sample<T extends Sample<T>> {
public static Sample<? extends Sample<?>> get() {
return new Sample<>();
}
public static void main(String... args) {
Sample<? extends Sample<?>> sample = Sample.get();
}
}
It compiles without warning and executes fine. However, if one tries to somehow define the inferred type of return new Sample<>(); in get() explicitly the compiler complains.
Up until now, I was under the impression that the diamond operator is just some syntactic sugar to not write explicit types and thus could always be replaced with some explicit type. For the given example, I was not able to define any explicit type for the return value to make the code compile. Is it possible to explicitly define the generic type of the return value or is the diamond-operator needed in this case?
Below are some attempts I made to explicitly define the generic type of the returned value with the corresponding compiler errors.
return new Sample<Sample> results in:
Sample.java:6: error: type argument Sample is not within bounds of type-variable T
return new Sample<Sample>();
^
where T is a type-variable:
T extends Sample<T> declared in class Sample
Sample.java:6: error: incompatible types: Sample<Sample> cannot be converted to Sample<? extends Sample<?>>
return new Sample<Sample>();
^
return new Sample<Sample<?>> results in:
Sample.java:6: error: type argument Sample<?> is not within bounds of type-variable T
return new Sample<Sample<?>>();
^
where T is a type-variable:
T extends Sample<T> declared in class Sample
return new Sample<Sample<>>(); results in:
Sample.java:6: error: illegal start of type
return new Sample<Sample<>>();
^
The JLS simply says:
If the type argument list to the class is empty — the diamond form <> — the type arguments of the class are inferred.
So, is there some inferred X that will satisfy the solution? Yes.
Of course, for you to explicitly define such an X, you'd have to declare it:
public static <X extends Sample<X>> Sample<? extends Sample<?>> get() {
return new Sample<X>();
}
The explicit Sample<X> is compatible with the return type Sample<? extends Sample<?>>, so compiler is happy.
The fact that return type is a messed up Sample<? extends Sample<?>> is an entirely different story.
Instantiating Generics with Wildcards
There's a couple problems here, but before delving into them, let me address your actual question:
Is it possible to explicitly define the generic type of the return value or is the diamond-operator needed in this case?
It is not possible to explicitly instantiate a Sample<? extends Sample<?>> (or a Sample<?> for that matter). Wildcards may not be used as type arguments when instantiating a generic type, though they may be nested within type arguments. For example, while it is legal to instantiate an ArrayList<Sample<?>>, you cannot instantiate an ArrayList<?>.
The most obvious workaround would be to simply return some other concrete type that is assignable to Sample<?>. For example:
class Sample<T extends Sample<T>> {
static class X extends Sample<X> {}
public static Sample<? extends Sample<?>> get() {
return new X();
}
}
However, if you specifically want to return a generic instantiation of the Sample<> class containing wildcards, then you must rely on generic inference to work out the type arguments for you. There are a few ways to go about this, but it usually involves one of the following:
Using the diamond operator, as you are doing right now.
Delegating to a generic method that captures your wildcard with a type variable.
While you cannot include a wildcard directly in a generic instantiation, it's perfectly legal to include a type variable, and that's what makes option (2) possible. All we have to do is ensure that the type variable in the delegate method gets bound to a wildcard at the call site. Every mention of the type variable the method's signature and body then gets replaced with a reference to that wildcard. For example:
public class Sample<T extends Sample<T>> {
public static Sample<? extends Sample<?>> get() {
final Sample<?> s = get0();
return s;
}
private static <T extends Sample<T>> Sample<T> get0() {
return new Sample<T>();
}
}
Here, the return type of Sample<T> get0() gets expanded to Sample<WC#1 extends Sample<WC#1>>, where WC#1 represents a captured copy of the wildcard inferred from the assignment target in Sample<?> s = get0().
Multiple Wildcards in a Type Signature
Now, let's address that method signature of yours. It's hard to tell for sure based on what code you've provided, but I would guess that a return type of Sample<? extends Sample<?>> is *not* what you really want. When wildcards appear in a type, each wildcard is distinct from all others. There is no enforcement that the first wildcard and second wildcard refer to the same type.
Let's say get() returns a value of type X. If it was your intention to ensure that X extends Sample<X>, then you have failed. Consider:
class Sample<T extends Sample<T>> {
static class X extends Sample<X> {}
static class Y extends Sample<X> {}
public static Sample<? extends Sample<?>> get() {
return new Y();
}
public static void main(String... args) {
Sample<?> s = Sample.get(); // legal (!)
}
}
In main, variable s holds a value that is a Sample<X> and a Y, but not a Sample<Y>. Is that what you'd intended? If not, I suggest replacing the wildcard in your method signature with a type variable, then letting the caller decide the type argument:
class Sample<T extends Sample<T>> {
static class X extends Sample<X> {}
static class Y extends Sample<X> {}
public static <T extends Sample<T>> Sample<T> get() { /* ... */ }
public static void main(String... args) {
Sample<X> x = Sample.get(); // legal
Sample<Y> y = Sample.get(); // NOT legal
Sample<?> ww = Sample.get(); // legal
Sample<?> wx = Sample.<X>get(); // legal
Sample<?> wy = Sample.<Y>get(); // NOT legal
}
}
The version above effectively guarantees that, for some return value of type A, the returned value extends Sample<A>. In theory, it even works when T is bound to a wildcard. Why? It goes back to wildcard capture:
In your original get method, the two wildcards could end up referring to different types. In effect, your return type was Sample<WC#1 extends Sample<WC#2>, where WC#1 and WC#2 are separate wildcards that are not related in any way. But in the example above, binding T to a wildcard captures it, allowing the same wildcard to appear in more than one spot. Thus, when T is bound to a wildcard WC#1, the return type expands to Sample<WC#1 extends Sample<WC#1>. Remember, there is no way to express that type directly in Java: it can only be done by relying on type inference.
Now, I said this works with wildcards in theory. In practice, you probably won't be able to implement get in such a way that the generic constraints are runtime-enforceable. That's because of type erasure: the compiler can emit a classcast instruction to verify that the returned value is, for example, both an X and a Sample, but it cannot verify that it's actually a Sample<X>, because all generic forms of Sample have the same runtime type. For concrete type arguments, the compiler can usually prevent suspect code from compiling, but when you throw wildcards into the mix, complex generic constraints become difficult or impossible to enforce. Buyer beware :).
Aside
If all this is confusing to you, don't fret: wildcards and wildcard capture are among the most difficult aspects of Java generics to understand. It's also not clear whether understanding these will actually help you with your immediate goal. If you have an API in mind, it might be best to submit it to the Code Review stack exchange and see what kind of feedback you get.
Coming from C++, some Java code looks very strange to me. Can someone explain to me what the following code fragment is supposed to do?
#SuppressWarnings("unchecked")
private static <T> Class<T> getGenericType(Class<?> clz) {
return (Class<T>) ((ParameterizedType) clz.getGenericSuperclass())
.getActualTypeArguments()[0];
}
I read this in a response to another question (see here) which I am trying to understand in detail.
Suppose you have a generic class Foo, like so:
public class Foo<T> {
T myVar;
}
In a lot of ways, this is like a template in C++. T is some type, could be String, could be Integer, could be Object, could be SomeTypeYouveNeverHeardOf, all depending on the code that uses Foo, not the code inside Foo.
And a subclass of it, Bar:
public class Bar extends Foo<String> {}
Bar is not a generic class, though it uses one as its superclass. It specifies that, for all instances of Bar, the type variable T is actually String, so the myVar instance variable it inherits from Foo will always be a String.
Now with that setup out of the way, let's turn to your method.
First, it takes a Class object as its parameter. Let's say we call it with Bar.class. The first thing your method does is call getGenericSuperclass() on it, which retrieves an object representing its superclass - Foo<String>.
Now, getGenericSuperclass() is declared to return a Type object, but the code is assuming that the class passed in has generic inheritance (that is, that it extends something like Foo<String> rather than just Foo), and in that case it will actually return a ParameterizedType. So it type casts it and calls getActualTypeArguments() to retrieve what the type arguments are - the String part of extends Foo<String>. This is returned as an array because in the more general case it could be dealing with things like extends ComplexObject<String, Integer, Foo, Object>.
Having acquired a size 1 array of Class objects, it then returns the array's first member. In this example, the returned value is String.class.
What this method does on my system, now that I test it, is throw an exception at runtime. So I guess what it does is "nothing useful."
public class GenericTest
{
public static void main( String[] args )
{
ArrayList<String> al = new ArrayList<>();
System.out.println( getGenericType( al.getClass() ) );
}
#SuppressWarnings( "unchecked" )
private static <T> Class<T> getGenericType( Class<?> clz )
{
return (Class<T>) ( (ParameterizedType) clz.getGenericSuperclass() )
.getActualTypeArguments()[0]; // line 28
}
}
run:
Exception in thread "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
at quicktest.GenericTest.getGenericType(GenericTest.java:28)
at quicktest.GenericTest.main(GenericTest.java:21)
C:\Users\Brenden\AppData\Local\NetBeans\Cache\8.1\executor-snippets\run.xml:53: Java returned: 1
BUILD FAILED (total time: 2 seconds)
I have a java interface like this
public interface MyInterface<T> {
public <V extends T> V get(String key, Bundle bundle);
}
please note the <V extends T> type parameter of the method.
Then I have a class MyFoo implements MyInterface
class MyFoo implements MyInterface<Object> { // Object because can be any type
#Override public <V> V get(String key, Bundle bundle) {
return new Other();
}
}
So when I now have a class like this:
class Bar {
public Other other;
public Other setOther(Other other);
}
Then I want to take MyFoo to set Other in a Bar instance:
MyFoo foo = new MyFoo();
Bar bar = new Bar();
bar.other = foo.get();
This works perfectly. Type can be determined by java generics. No additional cast is needed.
However, if I want to use bar.setOther() then the type can not be determined:
MyFoo foo = new MyFoo();
Bar bar = new Bar();
bar.setOther(foo.get()); // Fails
Then I get the following compile error:
error: incompatible types: Object cannot be converted to Other
I don't understand why this doesn't work for the bar.setOther( foo.get() ) method but works when accessing the field directly bar.other = foo.get()
Any idea how to solve that without adding an extra cast like bar.setOther( (Other) foo.get() )
In an assignment, the Java compiler knows what return type to use for the method by looking at the type that it is being assigned to. So to answer your question
Any idea how to solve that without adding an extra cast like
bar.setOther( (Other) foo.get() ) ?
This is a way in which you can do that:
Other value = foo.get();
bar.setOther(value);
There is another way, which looks worse but still doesn't have a cast:
bar.setOther(foo.<Other>get());
And a third alternative: switch to Java 8; in Java 8 you can just do bar.setOther(foo.get());.
For Java 7, the rules for this type inference are specified in JLS section 15.12.2.8:
If any of the method's type arguments were not inferred from the types
of the actual arguments, they are now inferred as follows.
If the method result occurs in a context where it will be subject to
assignment conversion (§5.2) to a type S, then [...]
A method result is subject to assignment conversion if it is used in an assignment expression.
Otherwise, the unresolved type arguments are inferred by invoking the
procedure described in this section under the assumption that the
method result was assigned to a variable of type Object.
If the unresolved type argument is used in a method whose result is not used in an assignment expression, then the type argument is interpreted as if the method result was assigned to a variable of type Object. So this means in this case that if the result of the get() method is not assigned to a variable, then the return type of the get() method is taken to be Object.
I am puzzled by generics. You can declare a field like:
Class<Collection<String>> clazz = ...
It seems logical that you could assign this field with:
Class<Collection<String>> clazz = Collection<String>.class;
However, this generates an error:
Syntax error on token ">", void expected after this token
So it looks like the .class operator does not work with generics. So I tried:
class A<S> { }
class B extends A<String> { }
Class<A<String>> c = B.class;
Also does not work, generates:
Type mismatch: cannot convert from Class<Test.StringCollection> to Class<Collection<String>>
Now, I really fail to see why this should not work. I know generic types are not reified, but in both cases it seems to be fully type safe without having access to runtime generic types. Anybody an idea?
Generics are invariant.
Object o = "someString"; // FINE!
Class<Object> klazz = String.class; // DOESN'T COMPILE!
// cannot convert from Class<String> to Class<Object>
Depending on what it is that you need, you may be able to use wildcards.
Class<? extends Number> klazz = Integer.class; // FINE!
Or perhaps you need something like this:
Class<List<String>> klazz =
(Class<List<String>>) new ArrayList<String>().getClass();
// WARNING! Type safety: Unchecked cast from
// Class<capture#1-of ? extends ArrayList> to Class<List<String>>
As for the non-reified at run-time case, you seem to have a good grasp, but here's a quote anyway, from the Java Tutorials on Generics, The Fine Print: A Generic Class is Shared by All Its Invocations:
What does the following code fragment print?
List <String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());
You might be tempted to say false, but you'd be wrong. It prints true, because all instances of a generic class have the same run-time class, regardless of their actual type parameters.
That is, there's no such thing as List<String>.class or List<Integer>.class; there's only List.class.
This is also reflected in the JLS 15.8.2 Class Literals
A class literal is an expression consisting of the name of a class, interface, array, or primitive type, or the pseudo-type void, followed by a . and the token class.
Note the omission of any allowance for generic type parameters/arguments. Furthermore,
It is a compile time error if any of the following occur:
The named type is a type variable or a parameterized type, or an array whose element type is a type variable or parameterized type.
That is, this also doesn't compile:
void <T> test() {
Class<?> klazz = T.class; // DOESN'T COMPILE!
// Illegal class literal for the type parameter T
}
Basically you can't use generics with class literals, because it just doesn't make sense: they're non-reified.
I agree with the other answers, and would like to explain one point further:
Class objects represent classes that are loaded into the JVM memory. Each class object is actually an in-memory instance of a .class file. Java generics are not separate classes. They are just a part of the compile-time type-checking mechanism. Therefore, they have no run-time representation in a class object.
There seems to be a lack in class literals in Java, there is no way to create class literals with generic information while this can be useful in certain cases. Therefore, the following code cannot be called because it is impossible to provide the class literal
class A<S> {}
<S> A<S> foo( Class<A<S>> clazz ) {}
A<String> a = foo( A<String>.class ) // error
However, my main problem was I could also not call it with a class B that extended A. This was caused by the invariance restrictions. This was solved by using a wildcard:
class A<S> {}
class B extends A<String> {}
<S> A<S> foo( Class<? extends A<S>> clazz ) { return null; }
void test () {
A<String> s = foo( B.class );
}
That said I have not found a reason what the underlying reason is that Class<A<S>>.class is invalid. Neither erasure nor bounds seem require that this is invalid.