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?
Related
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
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.
When a generic static method is written like this:
public static <T extends SuperClass> T foo() {
return new SupperClass();
}
, an incompatible types compilation error is thrown indicating that SuperClass cannot be converted to T.
On the other hand, when another generic static method is written like this:
public static <T extends SuperClass> void bar(T val) {
// do something....
}
the code compiles just fine when the function is called using: bar(new SuperClass()).
Also the same thing happens when using a generic class with the bounded generic type as it's type parameter,
whether it is the return type or a parameter of the function.
For example this causes the same compilation error to be thrown:
public static <T extends SupperClass> GenericClass<T> foo() {
return new GenericClass<SuperClass>();
}
indicating that GenericClass<SuperClass> cannot be converted to GenericClass<T>.However, this:
public static <T extends SuperClass> void bar(GenericClass<T> val) {
// do something....
}
compiles just fine when it is called using bar(new GenericClass<SuperClass>).
So to sum up the question, why can a generic method whose formal type is bounded with some supertype, and takes a parameter of this formal type, be called with an object of the supertype, while it can't return an object of the supertype when the return type is specified as the formal type ?
Thanks in advance.
The compiler is looking at the static type when doing compilation.
When you're trying to return a
new SupperClass()
object - the compiler must be sure that the static types are matched - and is doing so by converting from SupperClass to T.
When you'll do this casting yourself:
(T) new SupperClass()
it will give you a warning on unchecked cast - because technically you can program it but its successfulness depends on runtime types...
When you're returning void - you're returning nothing - so no casting needs to be done.
A comment on casting: casting up will always succeed (you can always loose information) casting down - depends on the dynamic type and not always succeed.
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.
This code seems to work fine
class Rule<T>
{
public <T>Rule(T t)
{
}
public <T> void Foo(T t)
{
}
}
Does the method type parameter shadow the class type parameter?
Also when you create an object does it use the type parameter of the class?
example
Rule<String> r = new Rule<String>();
Does this normally apply to the type parameter of the class, in the situation where they do not conflict? I mean when only the class has a type parameter, not the constructor, or does this look for a type parameter in the constructor? If they do conflict how does this change?
SEE DISCUSSION BELOW
if I have a function call
x = <Type Parameter>method(); // this is a syntax error even inside the function or class ; I must place a this before it, why is this, and does everything still hold true. Why don't I need to prefix anything for the constructor call. Shouldn't Oracle fix this.
All of your Ts are different, but you can only see it if you call your methods with the complete syntax:
For example, this code is valid:
new <Float>Rule<Integer>().<Character>Foo();
Just to make this easier to explain, let's assume your code is this:
class Rule<A>
{
public <B>Rule()
{
}
public <C> void Foo()
{
}
}
Then you can explicitly declare generic types like:
new <B>Rule<A>().<C>Foo();
If the types have the same name, the inner-most one will be chosen (the T on the method, not the class):
With this code, taking parameters:
class Rule<T>
{
public <T>Rule(T t)
{
}
public <T> void Foo(T t)
{
}
}
Then this is valid:
new <Float>Rule<Integer>(3.2f);
Note that T in the constructor is Float, not Integer.
Another example:
class Example<T> {
public <T> void foo1() {
// T here is the <T> declared on foo1
}
public void foo2() {
// T here is the <T> declared on the class Example
}
}
I found another question that deals with calling methods with explicit generic types without something before them. It seems like static imports and same-class method calls are the same. It seems like Java doesn't let you start a line with <Type> for some reason.
Does the method type parameter shadow the class type parameter?
The <T> declaration on constructor is not referred to class type. So yes, it shadow the class type parameter.
In this case it is used as a generic type param that you can use with the constructor, for example as argument. Try this constructor:
public <P> Rule(P arg1, P arg2) {
}
As you can see I define a type <P> and then I use it to be sure that the arguments will be of type P. In your case you are declaring a type that will be valid for the constructor without using it.
Look at this page.
Also when you create an object does it use the type parameter of the class?
Every generic type definition has is scope as a variable. So out of the constructor returns valid the class type.