To achieve.
A method signal should take
any Object N
any of the Object's superclasses Class<?super N>
<N>void
signal(N n, Class<?super N> n_super)
{
/*...*/
}
It should be ok to call
Object object=new Object();
signal(object, object.getClass());
since Object is a super type of object. But calling it gives a waring. In IDE words:
IntelliJ (Android Studio)
Wrong 2nd argument type. Found Class<? extends Object>, required: Class<? super Object>
Eclipse
The method signal(N, Class<? super N>)
is not applicable for the arguments (Object, Class<? extends Object>)
Questions.
Can the goal be achieved, the way I have tried, and if yes,
how can the warning be eliminated
Turning my comments into an answer.
Two approaches I can quickly think of:
Use Object.class directly here. signal(object, Object.class); compiles fine with no warnings on my java version.
Change your method signature to something like:
<N, M extends N> void signal(N n, Class<? super M> n_super)
which should let you call it the way you already are (as in signal(object, object.getClass());).
use something like this:
Object object=new Object();
Class c = object.getClass();
signal(object, c);
Related
How to safely cast Class<?> (returned by Class.forName()) to Class<Annotation> without issuing "Unchecked cast" warning?
private static Class<? extends Annotation> getAnnotation() throws ClassNotFoundException {
final Class<?> loadedClass = Class.forName("java.lang.annotation.Retention");
if (!Annotation.class.isAssignableFrom(loadedClass)) {
throw new IllegalStateException("#Retention is expected to be an annotation.");
}
#SuppressWarnings("unchecked")
final Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) loadedClass;
return annotationClass;
}
Multiple misconceptions need to be explained before delving into the answer.
You're using the wrong variance
final Class<Annotation> annotationClass = (Class<Annotation>) loadedClass;
This is actually illegal in any case. Try it:
Class<Number> n = Integer.class;
That won't compile.
Generics are invariant. It means that within the <>, you can't use a supertype as a standin for a subtype or vice versa.
Normal java (when <> are not involved) is covariant. Any subtype is a stand-in for one of its supertypes. This:
Number n = Integer.valueOf(5);
is perfectly legal java. But in generics world it isn't. If you want it to be, then, you have to opt into it: X extends Y is how you opt into covariance, and X super Y is how you opt into contravariance (contravariance is as if Integer i = new Number(); was legal - a SUPERtype can stand in for a subtype).
This is all because that's just how the universe ends up working out. If generics were naturally covariant, this would compile:
List<Integer> listOfInts = new ArrayList<>();
List<Number> listOfNums = listOfInts;
listOfNums.add(Double.valueOf(1.0));
int i = listOfInts.get(0);
but, follow along with your own eyes and you realize that code is a walking type violation. It shoves a non-integer into a list of integers. That's why opting into covariance or contravariances closes doors. If you opt into covariance, the add method is disabled *1:
List<? extends Number> list = new ArrayList<Integer>(); //legal
list.add(Integer.valueOf(5)); // will not compile
similarly, if you opt into contravariance, add works great, but get is disabled. 'disabled' in the type system sense: You can call it. But the expression list.get(i) would be of type Object:
List<? super Integer> list = new ArrayList<Number>(); // legal
list.add(Integer.valueOf(5)); // legal
Integer i = list.get(0); // won't compile
Object o = list.get(0); // this will.
With classes, where 'write' is not exactly clear, it's harder to see why Class<Annotation> c = SomeSpecificAnno.class; should fail to compile, but it does, so, that's important realization one.
Why are you using reflection here?
You can make class literals in java. This works great:
Class<? extends Number> c = Integer.class;
That's real java: You can stick .class at the end of any type and that will be an expression of type java.lang.Class, in fact, it's of type Class<TheExactThing>. So:
private static Class<? extends Annotation> getAnnotationType() {
return Retention.class;
}
works and compiles fantastically. I had to update the return type because as I explained above, returning the instance of j.l.Class that represents the Retention annotation for a method that is specced to return Class<Annotation> is as broken as returning an integer from a method that is specced to return a string.
The answer
If your code example is using java.lang.annotation.Retention as a stand-in, but your actual string here is a dynamic value that you do not know at compile time, the return Retention.class; option is off the table, then:
private static Class<? extends Annotation> getAnnotationType(String fqn) throws ClassNotFoundException {
return Class.forName(fqn).asSubclass(Annotation.class);
}
Again, do not use reflection unless there is no other way, and if you have the class in a string constant, generally you do not need reflection.
*1 ) You can call add, but only with a null literal; list.add(null); compiles, because null is trivially a valid value for any type. However, that's not particularly useful, of course.
Due to type erasure, generic type information is not accessible anymore at runtime. This means: Class<?> and Class<? extends Annotation> are indistinguishable at runtime - so you cannot do a runtime check to make the unchecked cast a checked one.
This means: you have to live with the warning (mind: a warning is not an error, it just means "this is problemattic, make sure you know what you're doing!).
Suppose I have an object Class<?> c. Is it possible to create a generic list with c as the generic parameter?
So something like this:
Class<?> c = doSomething();
List<c> list = new ArrayList<c>();
No that is impossible - at least your grammar will not compile. However, you may try to learn generics in Java, and see whether that helps your specific case, as this may be a A-B problem.
For example, this works:
<T> int yourFunction(List<T> items) {
T item = items.get(0);
// play with the item of type T, yeah!
}
For Class<? extend T> clazz, List<T> can be used for any instance created by clazz. For Class<? super T> clazz, List<T> should only contain instance that are compatibly with clazz.
For Class<?>, List<Object> is probably what you want. Any use of reflection, including Class is usually a mistake.
Why does the declaration look like this:
default <U extends Comparable<? super U>> Comparator<T> thenComparing(
Function<? super T, ? extends U> keyExtractor)
I understand most of it. It makes sense that U can be anything as long as it's comparable to a superclass of itself, and thus also comparable to itself.
But I don't get this part: Function<? super T, ? extends U>
Why not just have: Function<? super T, U>
Can't the U just parameterize to whatever the keyExtractor returns, and still extend Comparable<? super U> all the same?
Why is it ? extends U and not U?
Because of code conventions. Check out #deduper's answer for a great explanation.
Is there any actual difference?
When writing your code normally, your compiler will infer the correct T for things like Supplier<T> and Function<?, T>, so there is no practical reason to write Supplier<? extends T> or Function<?, ? extends T> when developing an API.
But what happens if we specify the type manually?
void test() {
Supplier<Integer> supplier = () -> 0;
this.strict(supplier); // OK (1)
this.fluent(supplier); // OK
this.<Number>strict(supplier); // compile error (2)
this.<Number>fluent(supplier); // OK (3)
}
<T> void strict(Supplier<T>) {}
<T> void fluent(Supplier<? extends T>) {}
As you can see, strict() works okay without explicit declaration because T is being inferred as Integer to match local variable's generic type.
Then it breaks when we try to pass Supplier<Integer> as Supplier<Number> because Integer and Number are not compatible.
And then it works with fluent() because ? extends Number and Integer are compatible.
In practice that can happen only if you have multiple generic types, need to explicitly specify one of them and get the other one incorrectly (Supplier one), for example:
void test() {
Supplier<Integer> supplier = () -> 0;
// If one wants to specify T, then they are forced to specify U as well:
System.out.println(this.<List<?>, Number> supplier);
// And if U happens to be incorrent, then the code won't compile.
}
<T, U> T method(Supplier<U> supplier);
Example with Comparator (original answer)
Consider the following Comparator.comparing method signature:
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, U> keyExtractor
)
Also here is some test classes hierarchy:
class A implements Comparable<A> {
public int compareTo(A object) { return 0; }
}
class B extends A { }
Now let's try this:
Function<Object, B> keyExtractor = null;
Comparator.<Object, A>comparing(keyExtractor); // compile error
error: incompatible types: Function<Object,B> cannot be converted to Function<? super Object,A>
TL;DR:
Comparator.thenComparing(Function< ? super T, ? extends U > keyExtractor) (the method your question specifically asks about) might be declared that way as an idiomatic/house coding convention thing that the JDK development team is mandated to follow for reasons of consistency throughout the API.
The long-winded version
„…But I don't get this part: Function<? super T, ? extends U>…“
That part is placing a constraint on the specific type that the Function must return. It sounds like you got that part down already though.
The U the Function returns is not just any old U, however. It must have the specific properties (a.k.a „bounds“) declared in the method's parameter section: <U extends Comparable<? super U>>.
„…Why not just have: Function<? super T, U>…“
To put it as simply as I can (because I only think of it simply; versus formally): The reason is because U is not the same type as ? extends U.
Changing Comparable< ? super U > to List< ? super U > and Comparator< T > to Set< T > might make your quandary easier to reason about…
default < U extends List< ? super U > > Set< T > thenComparing(
Function< ? super T, ? extends U > keyExtractor ) {
T input = …;
/* Intuitively, you'd think this would be compliant; it's not! */
/* List< ? extends U > wtf = keyExtractor.apply( input ); */
/* This doesn't comply to „U extends List< ? super U >“ either */
/* ArrayList< ? super U > key = keyExtractor.apply( input ); */
/* This is compliant because key is a „List extends List< ? super U >“
* like the method declaration requires of U
*/
List< ? super U > key = keyExtractor.apply( input );
/* This is compliant because List< E > is a subtype of Collection< E > */
Collection< ? super U > superKey = key;
…
}
„Can't the U just parameterize to whatever the keyExtractor returns, and still extend Comparable<? super U> all the same?…“
I have established experimentally that Function< ? super T, ? extends U > keyExtractor could indeed be refactored to the the more restrictive Function< ? super T, U > keyExtractor and still compile and run perfectly fine. For example, comment/uncomment the /*? extends*/ on line 27 of my experimental UnboundedComparator to observe that all of these calls succeed either way…
…
Function< Object, A > aExtractor = ( obj )-> new B( );
Function< Object, B > bExtractor = ( obj )-> new B( ) ;
Function< Object, C > cExtractor = ( obj )-> new C( ) ;
UnboundedComparator.< Object, A >comparing( aExtractor ).thenComparing( bExtractor );
UnboundedComparator.< Object, A >comparing( bExtractor ).thenComparing( aExtractor );
UnboundedComparator.< Object, A >comparing( bExtractor ).thenComparing( bExtractor );
UnboundedComparator.< Object, B >comparing( bExtractor ).thenComparing( bExtractor );
UnboundedComparator.< Object, B >comparing( bExtractor ).thenComparing( aExtractor );
UnboundedComparator.< Object, B >comparing( bExtractor ).thenComparing( cExtractor );
…
Technically, you could do the equivalent debounding in the real code. From the simple experimentation I've done — on thenComparing() specifically, since that's what your question asks about — I could not find any practical reason to prefer ? extends U over U.
But, of course, I have not exhaustively tested every use case for the method with and without the bounded ? .
I would be surprised if the developers of the JDK haven't exhaustively tested it though.
My experimentation — limited, I admit — convinced me that Comparator.thenComparing(Function< ? super T, ? extends U > keyExtractor) might be declared that way for no other reason than as an idiomatic/house coding convention thing that the JDK development team follows.
Looking at the code base of the JDK it's not unreasonable to presume that somebody somewhere has decreed: «Wherever there's a Function< T, R > the T must have a lower bound (a consumer/you input something) and the R must have an upper bound (a producer/you get something returned to you)».
For obvious reasons though, U is not the same as ? extends U. So the former should not be expected to be substitutable for the latter.
Applying Occam's razor: It's simpler to expect that the exhaustive testing the implementers of the JDK have done has established that the U -upper bounded wildcard is necessary to cover a wider number of use cases.
It seems like your question is regarding type arguments in general so for my answer I will be separating the type arguments you provided from the types they belong to, in my answer, for simplicity.
First we should note that a parameterized type of wildcard is unable to access its members that are of the respective type parameter. This is why, in your specific case the ? extends U can be substituted for U and still work fine.
This won't work in every case. The type argument U does not have the versatility and additional type safety that ? extends U has. Wildcards are a unique type argument in which instantiations of the parameterized types (with wildcard type arguments) are not as restricted by the type argument as they would be if the type argument was a concrete type or type parameter; wildcards are basically place holders that are more general than type parameters and concrete types (when used as type arguments). The first sentence in the java tutorial on wild cards reads:
In generic code, the question mark (?), called the wildcard, represents an unknown type.
To illustrate this point take a look at this
class A <T> {}
now let's make two declarations of this class, one with a concrete type and the other with a wild card and then we'll instantiate them
A <Number> aConcrete = new A <Integer>(); // Compile time error
A <? extends Number> aWild = new A<Integer>() // Works fine
So that should illustrate how a wildcard type argument does not restrict the instantiation as much as a concrete type. But what about a type parameter? The problem with using type parameters is best manifested in a method. To illustrate examine this class:
class C <U> {
void parameterMethod(A<U> a) {}
void wildMethod(A<? extends U> a) {}
void test() {
C <Number> c = new C();
A<Integer> a = new A();
c.parameterMethod(a); // Compile time error
c.wildMethod(a); // Works fine
}
Notice how the references c and a are concrete types. Now this was addressed in another answer, but what wasn't addressed in the other answer is how the concept of type arguments relate to the compile time error(why one type argument causes a compile time error and the other doesn't) and this relation is the reason why the declaration in question is declared with the syntax it's declared with. And that relation is the additional type safety and versatility wildcards provide over type parameters and NOT some typing convention. Now to illustrate this point we will have to give A a member of type parameter, so:
class A<T> { T something; }
The danger of using a type parameter in the parameterMethod() is that the type parameter can be referred to in the form of a cast, which enables access to the something member.
class C<U> {
parameterMethod(A<U> a) { a.something = (U) "Hi"; }
}
Which in turn enables the possibility of heap pollution. With this implementation of the parameterMethod the statement C<Number> c = new C(); in the test() method could cause heap pollution. For this reason, the compiler issues a compile time error when methods with arguments of type parameter are passed any object without a cast from within the type parameters declaring class; equally a member of type parameter will issue a compile time error if it is instantiated to any Object without a cast from within the type parameter's declaring class. The really important thing here to stress is without a cast because you can still pass objects to a method with an argument of type parameter but it must be cast to that type parameter (or in this case, cast to the type containing the type parameter). In my example
void test() {
C <Number> c = new C();
A<Integer> a = new A();
c.parameterMethod(a); // Compile time error
c.wildMethod(a); // Works fine
}
the c.parameterMethod(a) would work if a were cast to A<U>, so if the line looked like this c.parameterMethod((A<U>) a); no compile time error would occur, but you would get a run time castclassexection error if you tried to set an int variable equal to a.something after the parameterMethod() is called (and again, the compiler requires the cast because U could represent anything). This whole scenario would look like this:
void test() {
C <Number> c = new C();
A<Integer> a = new A();
c.parameterMethod((A<U>) a); // No compile time error cuz of cast
int x = a.something; // doesn't issue compile time error and will cause run-time ClassCastException error
}
So because a type parameter can be referenced in the form of a cast, it is illegal to pass an object from within the type parameters declaring class to a method with an argument of a type parameter or containing a type parameter. A wildcard cannot be referenced in the form of a cast, so the a in wildMethod(A<? extends U> a) could not access the T member of A; because of this additional type safety, because this possibility of heap pollution is avoided with a wildcard, the java compiler does permit a concrete type being passed to the wildMethod without a cast when invoked by the reference c in C<Number> c = new C(); equally, this is why a parameterized type of wildcard can be instantiated to a concrete type without a cast. When I say versatility of type arguments, I'm talking about what instantiations they permit in their role of a parameterized type; and when I say additional type safety I'm talking about about the inability to reference wildcards in the form of a cast which circumvents heapPollution.
I don't know why someone would cast a type parameter. But I do know a developer would at least enjoy the versatility of wildcards vs a type parameter. I may have written this confusingly, or perhaps misunderstood your question, your question seems to me to be about type arguments in general instead of this specific declaration. Also if keyExtractor from the declaration Function<? super T, ? extends U> keyExtractor is being used in a way that the members belonging to Function of the second type parameter are never accessed, then again, wildcards are ideal because they can't possibly access those members anyway; so why wouldn't a developer want the versatility mentioned here that wildcards provide? It's only a benefit.
import java.util.List;
import java.util.ArrayList;
interface Canine {}
class Dog implements Canine {}
public class Collie extends Dog {
public static void main(String[] args){
List<Dog> d = new ArrayList<Dog>();
List<Collie> c = new ArrayList<Collie>();
d.add(new Collie());
c.add(new Collie());
do1(d); do1(c);
do2(d); do2(c);
}
static void do1(List<? extends Dog> d2){
d2.add(new Collie());
System.out.print(d2.size());
}
static void do2(List<? super Collie> c2){
c2.add(new Collie());
System.out.print(c2.size());
}
}
The answer for this question tell that when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both. (Kathy and Bert)
What does it mean 'when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both' ?
As far as I know,
The method do1 has List<? extends Dog> d2 so d2 only can be accessed but not modified.
The method d2 has List<? super Collie> c2 so c2 can be accessed and modified and there is no compilation error.
Generic guidelines
You cannot add a Cat to a List<? extends Animal> because you don't know what kind of list that is. That could be a List<Dog> also. So you don't want to throw your Cat into a Black Hole. That is why modification of List declared that way is not allowed.
Similarly when you fetch something out of a List<? super Animal>, you don't know what you will get out of it. You can even get an Object, or an Animal. But, you can add an Animal safely in this List.
I pasted your code into my IDE. The following error was signalled inside do1:
The method add(capture#1-of ? extends Dog) in the type List is not applicable for the arguments (Collie)
This is, of course, as expected.
You simply cannot add a Collie to a List<? extends Dog> because this reference may hold for example a List<Spaniel>.
The answer for this question tell that when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both. (Kathy and Bert)
That's a fair first approximation, but not quite correct. More correct would be:
You can only add null to a Collection<? extends Dog> because its add method takes an argument of ? extends Dog. Whenever you invoke a method, you must pass parameters that are of a subtype of the declared parameter type; but for the parameter type ? extends Dog, the compiler can only be sure that the argument is of compatible type if the expression is null. However, you can of course modify the collection by calling clear() or remove(Object).
On the other hand, if you read from a Collection<? super Dog>, its iterator has return type ? super Dog. That is, it will return objects that are a subtype of some unknown supertype of Dog. But differently, the Collection could be a Collection<Object> containing only instances of String. Therefore
for (Dog d : collection) { ... } // does not compile
so the only thing we know is that instances of Object are returned, i.e. the only type-correct way of iterating such a Collection is
for (Object o : collection) { ... }
but it is possible to read from a collection, you just don't know what types of objects you will get.
We can easily generalize that observation to: Given
class G<T> { ... }
and
G<? extends Something> g;
we can only pass null to method parameters with declared type T, but we can invoke methods with return type T, and assign the result a variable of type Something.
On the other hand, for
G<? super Something> g;
we can pass any expression of type Something to method parameters with declared type T, and we can invoke methods with return type T, but only assign the result to a variable of type Object.
To summarize, the restrictions on the use of wildcard types only depend on the form of the method declarations, not on what the methods do.
I pasted your code into IDEONE http://ideone.com/msMcQ. It did not compile for me - which is what I expected. Are you sure you did not have any compilation errors?
Why can I use super only with wildcards and not with type parameters?
For example, in the Collection interface, why is the toArray method not written like this
interface Collection<T>{
<S super T> S[] toArray(S[] a);
}
super to bound a named type parameter (e.g. <S super T>) as opposed to a wildcard (e.g. <? super T>) is ILLEGAL simply because even if it's allowed, it wouldn't do what you'd hoped it would do, because since Object is the ultimate super of all reference types, and everything is an Object, in effect there is no bound.
In your specific example, since any array of reference type is an Object[] (by Java array covariance), it can therefore be used as an argument to <S super T> S[] toArray(S[] a) (if such bound is legal) at compile-time, and it wouldn't prevent ArrayStoreException at run-time.
What you're trying to propose is that given:
List<Integer> integerList;
and given this hypothetical super bound on toArray:
<S super T> S[] toArray(S[] a) // hypothetical! currently illegal in Java
the compiler should only allow the following to compile:
integerList.toArray(new Integer[0]) // works fine!
integerList.toArray(new Number[0]) // works fine!
integerList.toArray(new Object[0]) // works fine!
and no other array type arguments (since Integer only has those 3 types as super). That is, you're trying to prevent this from compiling:
integerList.toArray(new String[0]) // trying to prevent this from compiling
because, by your argument, String is not a super of Integer. However, Object is a super of Integer, and a String[] is an Object[], so the compiler still would let the above compile, even if hypothetically you can do <S super T>!
So the following would still compile (just as the way they are right now), and ArrayStoreException at run-time could not be prevented by any compile-time checking using generic type bounds:
integerList.toArray(new String[0]) // compiles fine!
// throws ArrayStoreException at run-time
Generics and arrays don't mix, and this is one of the many places where it shows.
A non-array example
Again, let's say that you have this generic method declaration:
<T super Integer> void add(T number) // hypothetical! currently illegal in Java
And you have these variable declarations:
Integer anInteger
Number aNumber
Object anObject
String aString
Your intention with <T super Integer> (if it's legal) is that it should allow add(anInteger), and add(aNumber), and of course add(anObject), but NOT add(aString). Well, String is an Object, so add(aString) would still compile anyway.
See also
Java Tutorials/Generics
Subtyping
More fun with wildcards
Related questions
On generics typing rules:
Any simple way to explain why I cannot do List<Animal> animals = new ArrayList<Dog>()?
java generics (not) covariance
What is a raw type and why shouldn’t we use it?
Explains how raw type List is different from List<Object> which is different from a List<?>
On using super and extends:
Java Generics: What is PECS?
From Effective Java 2nd Edition: "producer extends consumer super"
What is the difference between super and extends in Java Generics
What is the difference between <E extends Number> and <Number>?
How can I add to List<? extends Number> data structures? (YOU CAN'T!)
As no one has provided a satisfactory answer, the correct answer seems to be "for no good reason".
polygenelubricants provided a good overview of bad things happening with the java array covariance, which is a terrible feature by itself. Consider the following code fragment:
String[] strings = new String[1];
Object[] objects = strings;
objects[0] = 0;
This obviously wrong code compiles without resorting to any "super" construct, so array covariance should not be used as an argument.
Now, here I have a perfectly valid example of code requiring super in the named type parameter:
class Nullable<A> {
private A value;
// Does not compile!!
public <B super A> B withDefault(B defaultValue) {
return value == null ? defaultValue : value;
}
}
Potentially supporting some nice usage:
Nullable<Integer> intOrNull = ...;
Integer i = intOrNull.withDefault(8);
Number n = intOrNull.withDefault(3.5);
Object o = intOrNull.withDefault("What's so bad about a String here?");
The latter code fragment does not compile if I remove the B altogether, so B is indeed needed.
Note that the feature I'm trying to implement is easily obtained if I invert the order of type parameter declarations, thus changing the super constraint to extends. However, this is only possible if I rewrite the method as a static one:
// This one actually works and I use it.
public static <B, A extends B> B withDefault(Nullable<A> nullable, B defaultValue) { ... }
The point is that this Java language restriction is indeed restricting some otherwise possible useful features and may require ugly workarounds. I wonder what would happen if we needed withDefault to be virtual.
Now, to correlate with what polygenelubricants said, we use B here not to restrict the type of object passed as defaultValue (see the String used in the example), but rather to restrict the caller expectations about the object we return. As a simple rule, you use extends with the types you demand and super with the types you provide.
The "official" answer to your question can be found in a Sun/Oracle bug report.
BT2:EVALUATION
See
http://lampwww.epfl.ch/~odersky/ftp/local-ti.ps
particularly section 3 and the last paragraph on page 9. Admitting
type variables on both sides of subtype constraints can result in a
set of type equations with no single best solution; consequently,
type inference cannot be done using any of the existing standard
algorithms. That is why type variables have only "extends" bounds.
Wildcards, on the other hand, do not have to be inferred, so there
is no need for this constraint.
####.### 2004-05-25
Yes; the key point is that wildcards, even when captured, are only used
as inputs of the inference process; nothing with (only) a lower bound needs
to be inferred as a result.
####.### 2004-05-26
I see the problem. But I do not see how it is different from the problems
we have with lower bounds on wildcards during inference, e.g.:
List<? super Number> s;
boolean b;
...
s = b ? s : s;
Currently, we infer List<X> where X extends Object as the type of the
conditional expression, meaning that the assignment is illegal.
####.### 2004-05-26
Sadly, the conversation ends there. The paper to which the (now dead) link used to point is Inferred Type Instantiation for GJ. From glancing at the last page, it boils down to: If lower bounds are admitted, type inference may yield multiple solutions, none of which is principal.
The only reason is it makes no sense when declaring a type parameter with a super keyword when defining at a class level.
The only logical type-erasure strategy for Java would have been to fallback to the supertype of all objects, which is the Object class.
A great example and explanation can be found here:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#Why%20is%20there%20no%20lower%20bound%20for%20type%20parameters?
A simple example for rules of type-erasure can be found here:
https://www.tutorialspoint.com/java_generics/java_generics_type_erasure.htm#:~:text=Type%20erasure%20is%20a%20process,there%20is%20no%20runtime%20overhead.
Suppose we have:
basic classes A > B > C and D
class A{
void methodA(){}
};
class B extends A{
void methodB(){}
}
class C extends B{
void methodC(){}
}
class D {
void methodD(){}
}
job wrapper classes
interface Job<T> {
void exec(T t);
}
class JobOnA implements Job<A>{
#Override
public void exec(A a) {
a.methodA();
}
}
class JobOnB implements Job<B>{
#Override
public void exec(B b) {
b.methodB();
}
}
class JobOnC implements Job<C>{
#Override
public void exec(C c) {
c.methodC();
}
}
class JobOnD implements Job<D>{
#Override
public void exec(D d) {
d.methodD();
}
}
and one manager class with 4 different approaches to execute job on object
class Manager<T>{
final T t;
Manager(T t){
this.t=t;
}
public void execute1(Job<T> job){
job.exec(t);
}
public <U> void execute2(Job<U> job){
U u= (U) t; //not safe
job.exec(u);
}
public <U extends T> void execute3(Job<U> job){
U u= (U) t; //not safe
job.exec(u);
}
//desired feature, not compiled for now
public <U super T> void execute4(Job<U> job){
U u= (U) t; //safe
job.exec(u);
}
}
with usage
void usage(){
B b = new B();
Manager<B> managerB = new Manager<>(b);
//TOO STRICT
managerB.execute1(new JobOnA());
managerB.execute1(new JobOnB()); //compiled
managerB.execute1(new JobOnC());
managerB.execute1(new JobOnD());
//TOO MUCH FREEDOM
managerB.execute2(new JobOnA()); //compiled
managerB.execute2(new JobOnB()); //compiled
managerB.execute2(new JobOnC()); //compiled !!
managerB.execute2(new JobOnD()); //compiled !!
//NOT ADEQUATE RESTRICTIONS
managerB.execute3(new JobOnA());
managerB.execute3(new JobOnB()); //compiled
managerB.execute3(new JobOnC()); //compiled !!
managerB.execute3(new JobOnD());
//SHOULD BE
managerB.execute4(new JobOnA()); //compiled
managerB.execute4(new JobOnB()); //compiled
managerB.execute4(new JobOnC());
managerB.execute4(new JobOnD());
}
Any suggestions how to implement execute4 now ?
==========edited =======
public void execute4(Job<? super T> job){
job.exec( t);
}
Thanks to all :)
========== edited ==========
private <U> void execute2(Job<U> job){
U u= (U) t; //now it's safe
job.exec(u);
}
public void execute4(Job<? super T> job){
execute2(job);
}
much better, any code with U inside execute2
super type U becomes named !
interesting discussion :)
I really like the accepted answer, but I would like to put a slightly different perspective on it.
super is supported in a typed parameter only to allow contravariance capabilities. When it comes to covariance and contravariance it's important to understand that Java only supports use-site variance. Unlike Kotlin or Scala, which allow declaration-site variance. Kotlin documentation explains it very well here. Or if you're more into Scala, here's one for you.
It basically means that in Java, you can not limit the way you're gonna use your class when you declare it in terms of PECS. The class can both consume and produce, and some of its methods can do it at the same time, like toArray([]), by the way.
Now, the reason extends is allowed in classes and methods declarations is because it's more about polymorphism than it is about variance. And polymorphism is an intrinsic part of Java and OOP in general: If a method can accept some supertype, a subtype can always safely be passed to it. And if a method, at declaration site as it's "contract", should return some supertype, it's totally fine if it returns a subtype instead in its implementations