Trying to understand the role of super wildcard in Java Generics - java

I was going through this article to understand the role of super wildcard in generics. I understood how extends works, but I am having difficulty understanding super
I have a ClassA that is extended by ClassB, making ClassA super class of ClassB.
If I understood the article correctly, the <? super ClassB> would allow any Class that is a super type of ClassB.
I have the following code
GenericMethod.java
public class GenericMethod<T> {
private List<T> list;
public GenericMethod() {
list = new ArrayList<>();
}
public void add(T t) {
list.add(t);
}
public T get(int index) {
return list.get(index);
}
}
Driver.java
public class Driver {
public static void main(String[] args) {
GenericMethod<? super ClassB> genericMethod = new GenericMethod<>();
ClassA classA = new ClassA();
genericMethod.add(classA); // Compile-time error here
}
}
Error
The method add(capture#1-of ? super ClassB) in the type GenericMethod<capture#1-of ? super ClassB> is not applicable for the arguments (ClassA)
I don't understand where I am going wrong. When I instantiated the GenericMethod class, I already declared that it would accept any value that is a super type of ClassB with the declaration <? super ClassB>. Thus, the T inside the GenericMethod class should accept all classes that ClassB extends.
Why does the add method throw the compile-time error then? Shouldn't the method add already know that it's being passed a perfectly compatible type?

The ? super clause is a lower-bound wildcard. But the bound is on the type parameter that is inferred, not a restriction on the types of arguments that can be passed to a method that takes a parameter of that generic type.
When you say <? super ClassB>, you indicate that the type parameter can be ClassB or any supertype, e.g. ClassA or Object.
The compiler must treat the add method as if it could be any of these signatures:
add(Object t)
add(ClassA t)
add(ClassB t)
(There could be other types if ClassA inherited directly from another class instead of Object).
The compiler must reject a ClassA as an argument to add because the type parameter could be inferred as ClassB. It's legal to assign a GenericMethod<ClassB> to your genericMethod variable.
GenericMethod<? super ClassB> genericMethod = new GenericMethod<ClassB>();
But it doesn't make sense to be able to pass a ClassA to a method that expects a ClassB.
In fact that is what is inferred by the diamond operator - ClassB.
Your confusion is in the conflation of two concepts: what type parameters are allowed and what types of objects are allowed in methods that use a type parameter. Using a wildcard restricts the type parameter, but the method still accepts types that are the type parameter or a subtype.

By declaring GenericMethod<? super ClassB> you are declaring the type is an unknown type that is a super-class of ClassB (or ClassB itself). And asking the compiler to only allow subtypes of this unknown type to be added to the list.
The only compatible subtypes the compiler knows are ClassB and any subtypes of ClassB. When creating instances, it is usually better to avoid wildcards.
For method parameters, wildcards give you more flexibility on what can be accepted. Use PECS (producer=extends, consumer=super) to determine which to use.
See Slide 5

Related

Return concrete object to generic method with generic Map parameter during override

I have a generic method with generic Map parameter. I want to override it and use concrete subtype, but without cast I am not able to return my subtype
public interface GarrageSimple {
<T extends Car> T getCar(Map<String, T> c);
}
Implementation is below which gives error as: Incompatible types. Required: T Found: Bmw
public class GarrageSimpleImpl implements GarrageSimple {
#Override
public <T extends Car> T getCar(Map<String, T> c) {
return new Bmw();
}
}
Actually, Bmw is a subclass of Car which means a type of T. When I cast it like * return (T)new Bmw();* it works with a warning that Unchecked cast Bmw to T
I know that it is because I take a risk to return concrete subclass while method signature is generic, but how I can change my method to make it concrete and not break override signature?
If I can, in that case, it should work as expected.
PS: I don't want to make my class generic, I just need to change my method signature to make it concrete during implementation.
Thanks
No, this is not valid. What would happen if someone with a GarrageSimpleImpl reference called it with a different class extending Car? Generics are all about compile time safety and that's why you get this error.
But you can do it like so,
public interface GarrageSimple<T extends Car> {
T getCar(Map<String, T> c);
}
public class GarrageSimpleImpl implements GarrageSimple<Bmw> {
#Override
public Bmw getCar(Map<String, Bmw> c) {
return new Bmw();
}
}
You can't. A generic method (a method which declares its own type parameters) means that your method must work no matter what the type parameters (here T) are. The method cannot choose what T to use. The caller can call your method with T being anything within the bounds of T, and your method must work correctly with whatever T is without choice.
A subclass overriding a method in a superclass means that the subclass method must be a valid substitute for the overridden method, which means that it must work in all situations that the overridden method can be called in. Since the overridden method is generic, and works for any T, your subclass method that overrides it must also work for any T. Your method does not work for any T -- it fails for any T that is not Bmw.

Need to cast the class object with parent class's parent object in java

I need to cast the class object with parent class's parent object in java and same behavior is working in C#.
In C# I can cast the class object to parent class's parent object.
C# Code:
In java I cant achieve this behavior.
Java Code:
How to achieve this in java?.Thanks in advance
Generic type parameters in Java are invariant. That means Iterable<Cat> is not a subtype of Iterable<Species> (nor vice versa) even though Cat is a subtype of Species.
C# allows the generic type to declare its type parameters as invariant, covariant, or contravariant. If you declare Foo<out T>, it will be covariant, i.e. Foo<A> is a subtype of Foo<B> if A is a subtype of B; and if you declare Foo<in T>, it will be contravariant, i.e. Foo<B> is a subtype of Foo<A> if A is a subtype of B. IEnumerable is declared with its type parameter being covariant (i.e. with <out T>, so IEnumerable<Cat> can be a subtype of IEnumerable<Species>.
Covariance and contravariance is achieved in Java using wildcards. ? extends wildcards are covariant, while ? super wildcards are contravariant. There is the PECS rule (Producer extends, Consumer super) that guides you in using them. The Iterable interface is effectively a producer of its type parameter T (it has a method which returns an Iterator<T>, which in turn has a method which returns a T; and there are no methods that take a T as an argument); therefore, it should always be used with a ? extends wildcard. So you should use Iterable<? extends Something> instead of Iterable<Something>.
So if you declare Animal like this:
abstract class Animal extends Species {
public Iterable<? extends Cat> elements();
}
Then you can use it like this:
Iterable<? extends Species> childNodes;
childNodes = (currentElement != null) ? currentElement.elements() : null;
By the way, the IEnumerable interface in C# is likewise effectively a producer of its type parameter, and that's why they declared it as covariant.
Java and C# are different, let me explain you with the code:
package test;
class A {
{
System.out.println(1);
}
}
class B extends A {
{
System.out.println(2);
}
}
class C extends B {
{
System.out.println(3);
}
}
public class MainClass {
public static void main(String[] args) {
A c = new C();
}
}
Here we can make the object of parent class and instantiate or initialize that object with the child class but reverse is not true.
i.e C c = new A();
This line will throw java.lang.ClassCastException.
It is because in java the child class can use the methods of the parent class but the parent class cannot use the methods of the child class in general, but there are some tricks which can serve this purpose.
In general one can use the things of there parents but parents are not allowed to use the things of the children, you can think it like that.

Name clash when overriding method of generic class

I'm trying to understand the name clash error I get with the following code:
import java.util.*;
import javax.swing.*;
class Foo<R extends Number> {
public void doSomething(Number n, Map<String, JComponent> comps) {
}
}
class Bar extends Foo {
public void doSomething(Number n, Map<String, JComponent> comps) {
}
}
Error message:
error: name clash: doSomething(Number,Map<String,JComponent>) in Bar
and doSomething(Number,Map<String,JComponent>) in Foo have the same
erasure, yet neither overrides the other
I know I can fix it by either remove the generic type from Foo, or by changing the Bar declaration to class Bar extends Foo<Integer>; what I want to know is why this error occurs in the specific case, but goes away if I remove the comps parameter from each method. I've done some reading about type erasure, but it still appears to me that both methods should have the same erasure with or without generics, and therefore be a valid override in either case. (Note that I haven't even used the generic parameter anywhere yet, which is why I'm so surprised.)
I know that I've added generic types to parent classes before but only got warnings about the subclasses, not errors. Can anyone explain this scenario?
Luiggi is right in the comments. This is a consequence of raw types.
The supertype of a class may be a raw type. Member accesses for the
class are treated as normal, and member accesses for the supertype are
treated as for raw types. In the constructor of the class, calls to
super are treated as method calls on a raw type.
This applies when invoking a supertype method, but also when overriding one.
Take for example, the following
class Bar extends Foo {
public Bar() {
doSomething(1, new HashMap<Number, String>());
}
}
You'll notice that it compiles, even though HashMap<Number, String> is not a type that is assignable to Map<String, JComponent>.
The type of a constructor (§8.8), instance method (§8.4, §9.4), or
non-static field (§8.3) of a raw type C that is not inherited from its
superclasses or superinterfaces is the raw type that corresponds to
the erasure of its type in the generic declaration corresponding to C.
(Note that C in our case is Bar.)
And the same thing happens when trying to override a method. When trying to override the Foo#doSomething(..) method, your Bar class is actually seeing it declared as
public void doSomething(Number n, Map comps) {
}
In other words, every usage of type parameters is erased. So attempting to declare the method
public void doSomething(Number n, Map<String, JComponent> comps) {
}
in the subtype Bar is actually an attempt at overloading, not overriding. And this fails because of type erasure. The proper override, which you can verify with #Override, is
public void doSomething(Number n, Map comps) {
}
Further reading:
What is a raw type and why shouldn't we use it?

Genereic class and static generic method

I'm writing generic class
public class SomeClass<T> {
public static <T extends Comparable<? super T>> T min(Collection<? extends T> c) {
T min = c.iterator().next();
for (T element : c)
if (element.compareTo(min) < 0)
min = element;
return min;
}
}
public class Main {
public static void main(String[] args) {
SomeClass<Integer>.min(Arrays.asList(1, 2, 3)); // compile-time error
SomeClass.min(Arrays.asList(1, 2, 3)); // ok
}
}
In generic class SomeClass and generic method SomeMethod type-parameter T is the same or defference?
Why we have compile time-error on the string SomeClass<Integer>.min(Arrays.asList(1,2,3));?
The class declaration
public class SomeClass<T>
defines a generic class, where <T> specifies the type parameter (also called type variable). This introduces the type variable, T, that can be used anywhere inside the class.
And the method declaration:
public static <T extends Comparable<? super T>> T min(Collection<? extends T> c) {
...
}
defines a generic method. Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared.
Now if you want to call the generic method min, you need to call:
SomeClass.<Integer>min(Arrays.asList(1,2,3));
It's different, and you should never write code like this, specifically because of the possibility for confusion. Always use different type variables on classes and methods inside those classes.
T is for Type and you are accessing static method which has nothing to do with T.
Either use like this
SomeClass<Integer> a = new SomeClass<Integer>();
a.min(Arrays.asList(1, 2, 3));
OR
SomeClass.min(Arrays.asList(1, 2, 3));
The two Ts are different: the fist one is a parameter for the class (and is unused) and the second one is specific to the method. Since the method is static, the class parameter doesn’t affect it.
When you write SomeClass<Integer>.min(Arrays.asList(1, 2, 3));, you get an error because it doesn’t make sense to add a parameter to SomeClass since no object of that class is instanciated. SomeClass is only used to tell the compiler that you want to call a static method from that class. You can add a parameter to the method with SomeClass.<Integer>min(Arrays.asList(1, 2, 3));, but you don't have to since the compiler can infer the type here.
So what the compile message told you was that it's a syntax error. Why it's invalid syntax is easy to understand. You are trying to call a method on a type. That means you are calling a static method. Static methods are not within the scope of generic type parameters. So it is not allowed to put a generic type argument on the left side.
If you want the technical reason, it's because your method call expression is not one of the forms that's allowed by the syntax. The closest form to yours is TypeName . NonWildTypeArguments Identifier ( ArgumentListopt ). But TypeName (which is defined here) must be an identifier or package-qualified identifier. It does not allow brackets.

Class<? extends T> usage

I want to know what is the usage of having such syntax. I have written a simple program as below,
public class Sample{
public static void main(String[] args) {
Class<? extends Collection> someCollectionClass = someMethod();
}
public static ArrayList someMethod() {
return new ArrayList();
}
}
Why it shows compilation error as,
- Collection is a raw type. References to generic type Collection<E> should be
parameterized
- Type mismatch: cannot convert from ArrayList to Class<? extends Collection>
In here does that Class expect a class (Its actually the object that it accept know, Is it?) which does extends the Collection. So whats wrong with ArrayList?
Also is it a valid if syntax if i declare something as Class<? extends T>
Because ArrayList is not a Class. Perhaps you mean someMethod().getClass()?
The first error message stems from the type parameter in your declaration:
Class<? extends Collection> someCollectionClass = someMethod();
The type Collection is what is called a "raw type" in Java, because the interface Collection takes a type parameter, but none is given in the declaration. The same applies to the unadorned use of ArrayList.
However, The declaration doesn't actually do what I guess you think it does... You actually declare a variable named someCollectionClass, to which you can assign instances of type Class<? extends Collection>, i.e., (reflection) class descriptors (of a particular type). The second error message is the compiler complaining about that: an instance of type ArrayList (returned by someMethod) cannot be assigned to a variable, whose type is declared as "allow only instances of Class<...> (which is a special Java run-time type describing classes).
Collection someCollection = someMethod();
someMethod is returning an instance of type List, you are trying to assign it to a variable which is expecting the Collection class not an instance. The following code should work.
Collection someCollectionClass = someMethod();

Categories