This question already has answers here:
interface and a class. name clash: same erasure, yet neither overrides other
(3 answers)
Closed 3 years ago.
I have the following structure
public interface A <T extends B> {
List<String> getVals();
void setVals(List<String> vals);
T getContext();
void setContext(T context);
}
public abstract class C <T extends B> implements A {
protected T context;
//Some code
}
public class Regex <T extends B> extends C <T> {
public List<String> getVals() {
//Some code
}
public void setVals(List<String> vals) {
//Some code
}
}
The thing is that, when I compile, I'm getting the following error:
Regex.java:[53,15]
name clash: setVals(java.util.List<java.lang.String>) in Regex and setVals(java.util.List<java.lang.String>) in A have the same erasure, yet neither overrides the other
Why is this? If I ask Intellij to "do the overwrite" for me and it replaces the setVals(List vals) with setVals(List vals) instead.
public void setVals(List vals) {
//Some code
}
It shouldn't be, "exactly the same definition of the method"?
Sorry in advance for my lack of interfaces knowledge
I am guessing you want to implement parameterized type but are actually implementing the raw one.
Replace
public abstract class C <T extends B> implements A
with
public abstract class C <T extends B> implements A<T>
An interesting observation to note here is, that the function setVals is not even using the type parameter T, even then there is a name clash. So it is not obvious at first why this is happening, and why simply extending, A<T> works. At least not to me.
The answer lies here,
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.
and
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.
C in our case is also coincidentally C.
So without extending A<T> from Cs perspective the signature of A is,
void setVals(List vals)
and the compiler doesn't see void setVals(List<String> vals) in Regex as overriding that of A, instead it is considering it overloading, and that too an illegal overload because of type erasure. (You can't overload foo(List) with foo(List<String>)
Related
I've a use case :
inteface A{
get(EnumSet<?> fetchModes);
}
class B implements A{
//Here FetchMode is an enum
get(EnumSet<FetchMode> fetchMode){
//Some logic here
}
}
But it's throwing compile time error :
Method get of class B has the same erasure as get(EnumSet fetchMode) of type A but doesn't override it.
I had read that Enums cannot be generic but is there any way to implement this usecase ?
(Basically want the EnumSet to be generic and different implementations can pass different Enums)
A method can override another if the argument types match exactly, but yours doesn't. Eventhough EnumSet<FetchMode> is a subtype of EnumSet<?>, they are not exactly the same. You are not overriding the super class method get(EnumSet<?> fetchModes); in your subclass, rather you are overloading it with a different parameter type. Both of these has the same signature due to erasure when you inspect or decompile the bytecode which is get(EnumSet fetchModes) and your compiler starts complaining.
This is covered in JLS §8.4.8.1:
A class cannot have two member methods with the same name and type
erasure
A naive attempt at fixing the problem is to change the parameter type such that it is compatible with that of the super class, overriding the method properly in your sub class.
#Override
public void get(EnumSet<?> fetchModes) {
}
Though this fixes the compiler error after a fashion, it is still not elegant since it allows your EnumSet to store any Object. But ideally you may want it to store only some subtype of Enum. This idiom supports that.
What you have to do is declare a generic interface with bounded type parameter and then implement it by overriding the method properly as shown below.
public interface A<E extends Enum<E>> {
void get(EnumSet<E> fetchModes);
}
public class B implements A<FetchMode> {
#Override
public void get(EnumSet<FetchMode> fetchModes) {
}
}
try this you have to make the generic type extends Enum:
public class B implements A<FetchMode> {
//Here FetchMode is an enum
public void get(EnumSet<FetchMode> fetchMode){
//Some logic here
}
}
}
interface A<T extends Enum<T>> {
void get(EnumSet<T> fetchModes);
}
Suppose there is classes/interfaces hierarchy:
class A<T>{
T method(T t){
return t;
}
}
class B<T> extends A{
T method(T t){ // method(T)' in 'B' clashes with 'method(T)' in 'A'; both methods have same erasure, yet neither overrides the other
return t;
}
}
As we see there are a compiler error.
I've never came across the rules how to treat generics when inherit. What are the restrictions?
(please don't be confused with inheritance IN generic type itself, i'm asking about inheritance in original classes
also don't be confused with "What is a raw type", I know the raw types, at this question I wanna figure out what are the rules for inheritance)
also don't be confused thinking I wanna fix this error. Of course class B extends A fix it. My question is about: "where can I read the restrictions?"
You will have to use T in your class definition, so that the T wildcard gets bound to the same generic type in both A and B, resolving the conflict:
class B<T> extends A<T>
You didn't prolong A's T, you just introduced a new type parameter T for B. Those Ts are different unless you write B<T> extends A<T>:
class B<T> extends A<T> {
#Override
T method(T t) {
return t;
}
}
Both A's T and B's T would be erased to Object, the class B would contain 2 methods Object method(Object t).
Example 8.4.8.3-4. Erasure Affects Overriding
A class cannot have two member methods with the same name and type
erasure:
class C<T> {
T id (T x) {...}
}
class D extends C<String> {
Object id(Object x) {...}
}
https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#d5e14416
To read: 8.4.8.3. Requirements in Overriding and Hiding
You compile error is just as #f1sh says.
Now as to the rules,
They will have the same rules be applied to them. This would include:
Type eraser
Upper, lower, and Unbounded types
Also, generics can be applied to individual methods. How and when you use them depends on what you are trying to do.
For more information https://www.tutorialspoint.com/java/java_generics.htm
Disclaimer: I'm new to Java generics and collections.
Background: I've studied the basics of Java Generics here and here. Now I'm trying to understand how they apply to Hadoop's Mapper (public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>)
Problem: Until today, I had only seen placeholders in the class definition (public class OrderedPair<K,V> implements Pair<K,V>), not concrete classes (public class Me extends Thing<String,Integer,Character>).
Question: So in general, if I have this...
public class Me extends Thing<String,Integer,Character>
...what does "extends Thing<String,Integer,Character>" mean? It seems that I'm "extending" Thing--that is, the Me subclass inherits the methods of the Thing superclass. Is that inheritance different from Thing<String,String,String>?
Clarification: Put another way, what is the difference between extending a class without generics (e.g., public class X extends Y) and with generics (public class X extends Y<a,b,c>)?
A generic class can have different types that it is used with (you choose which type to use it with when you instantiate an object of it). If you extend a generic class and do put concrete types there, as you did here: public class Me extends Thing<String,Integer,Character> that means that Me is extending Thing, but Thing is not generic anymore since it is now bound to the given types.
Previously you could instantiate Thing so:
Thing<String, Character, Integer> myThing = new Thing<>();
but Me is bound now, you can not choose types for it anymore.
Me myMe = new Me();
You could also extend Thing with actual generic types, so your Me class would be still generic.
public class <T, K, V> Me extends Thing<T, K, V>
...
// init with
Me<String, Character, Integer> myMe = new Me<>();
With this you can initialize Me anywhere with given generic types, that will be passed to the generic Thing.
You can also do it partially. So some types of Thing are fixed, and some will be free to choose at the time you instantiate Me.
public class <T> Me extends Thing<String, T, String>
...
// init with
Me<String> myMe = new Me<>();
When extending a class that has a generic type, you can choose to specify the type under which the subclass will be working. For example, class MyList extends ArrayList<String> will have the ArrayList methods, but will be specific to Strings.
In the same manner, when you extend Mapper, you need to specify what types of parameters the mapper will be working on. You do that by providing the type parameters Mapper declares.
Clarification: Put another way, what is the difference between
extending a class without generics (e.g., public class X extends Y)
and with generics (public class X extends Y<a,b,c>)?
The difference is that in the second case (generic class), you have to conform to the constraints of the types specified by the generic class if you want to compile fine or else you should declare a raw subclass.
Generally, the types specified by a generic class are used by the method of it.
So defining them correctly matters.
You have mainly 3 cases.
Takes this generic class declaration that specifies 3 parameters and that uses them in a myMethod() method :
public class Y <A extends AClass,B extends BClass, C extends CClass> {
public void myMethod(A a, B b, C c){
...
}
}
1) Your subclass is a raw class :
public class Z extends Y {
....
}
In this case, it compiles fine but with warning.
You lose benefits of generic in method invocations.
The compiler will consider the method of Z with this signature :
public void myMethod(Object a, Object b, Object c){
...
}
2) You subclass is a generic compliant subclass :
public class Z extends Y<ASubClass,BSubClass,CSubClass> {
....
}
The compiler will consider the method of Z with this signature :
public void myMethod(ASubClass a, BSubClass b, CSubClass c){
...
}
3) Your subclass is a generic class but not compliant with the parameters specified by the parent class, you have a compilation error.
public class Z extends Y<BSubClass,ASubClass,CSubClass> {
....
}
If the supertype uses generics, then, as for any generic type, referring to it without generic parameters is the bad mistake of using raw types. You must either use a generic type variable or concrete type for each type variable in the generic type. For example, a variable would be like
Thing<String, Integer, Character> thing = new Thing<>();
Naturally that is different from
Thing<String, String, String> thing = ...
because it handles different types!
Same thing holds for inheritance. In your case you're locking down the type parameters to concrete types.
public class SomeThing extends Thing<String, Integer, Character> { ...
locks down the types that SomeThing can handle, so that SomeThing is actually not a generic class.
I need to instantiate an object with an unknown generic type and then apply a generic method to it. Here is where I stand :
public static void main(String[] args)
{
BarIf bar = newRandomBarImpl();
Foo foo1 = newFooBar(bar.getClass()); // warning
Foo<?> foo2 = newFooBar(bar.getClass()); // error
Foo<? extends BarIf> foo3 = newFooBar(bar.getClass()); // error
foo1.doSomething(bar); // warning
foo2.doSomething(bar); // error
foo3.doSomething(bar); // error
}
static <T extends FooIf<S>, S extends BarIf> T newFooBar(Class<S> barClass){}
static <T extends BarIf> T newRandomBarImpl(){}
interface FooIf<T extends BarIf>
{
public void doSomething(T t);
}
interface BarIf{}
class Foo<T extends BarIf> implements FooIf<T>
{
public void doSomething(T t){}
}
The strange thing is that for foo2 and foo3, the newFooBar() method returns FooIf rather than Foo. I guess the type inference is messy. But I can't pass the method generic parameters since I don't know the Bar type.
What I would need is Foo<bar.getClass()>. Is there a way to do it?
I tried using TypeToken but I end up with a T type rather than the actual Bar type. Any chance using that?
First of all, a declaration like
static <T extends BarIf> T newRandomBarImpl(){}
is nonsense. It basically says “whatever the caller substitutes for T, the method will return it”. In other words, you can write
ArbitraryTypeExtendingBarIf x=newRandomBarImpl();
without getting a compiler warning. Obviously, that can’t work. newRandomBarImpl() doesn’t know anything about ArbitraryTypeExtendingBarIf. The method name suggests that you actually want to express that newRandomBarImpl() can return an arbitrary implementation of BarIf, but that’s an unnecessary use of Generics,
BarIf newRandomBarImpl(){}
already expresses that this method can return an arbitrary subtype of BarIf. In fact, since BarIf is an abstract type, this method must return a subtype of BarIf and it’s nowhere specified which one it will be.
The same applies to the declaration
static <T extends FooIf<S>, S extends BarIf> T newFooBar(Class<S> barClass){}
It also claims that the caller can choose which implementation of FooIf the method will return. The correct declaration would be
static <S extends BarIf> FooIf<S> newFooBar(Class<S> barClass){}
as the method decides which implementation of FooIf it will return, not the caller.
Regarding your other attempts to deal with FooIf, you can’t work this way using a type parametrized with a wildcard, nor can you fix it using Reflection. But you can write generic code using a type parameter:
public static void main(String[] args)
{
BarIf bar = newRandomBarImpl();
performTheAction(bar.getClass(), bar);
}
static <T extends BarIf> void performTheAction(Class<T> cl, BarIf obj) {
FooIf<T> foo=newFooBar(cl);
foo.doSomething(cl.cast(obj));
}
static <S extends BarIf> FooIf<S> newFooBar(Class<S> barClass){}
static BarIf newRandomBarImpl(){}
interface FooIf<T extends BarIf> {
public void doSomething(T t);
}
interface BarIf{}
The method performTheAction is generic, in other words, works with an unknown type expressed as type parameter T. This method can be invoked with an unknown type ? extends BarIf as demonstrated in the main method.
However, keep in mind, that every reference to a type X implies that the referred object might have a subtype of X without the need to worry about it.
You can simply use the base class BarIf here, regardless of which actual subtype of BarIf the object has:
BarIf bar = newRandomBarImpl();
FooIf<BarIf> foo=newFooBar(BarIf.class);
foo.doSomething(bar);
Note that when you want to use methods of the actual implementation type Foo, not specified in the interface, you will have to cast the FooIf to Foo. You can cast a FooIf<BarIf> to Foo<BarIf> without a warning as the generic type conversion is correct if Foo<X> implements FooIf<X>.
However, it can fail at runtime as the method newFooBar is not required to return an instance of Foo rather than any other implementation of FooIf. That’s why an explicit type cast is the only correct solution as it documents that there is an assumption made about the actual runtime type of an object. All other attempts will generate at least one compiler warning somewhere.
This is an existing interface:
public interface MyInterface<T, U> {
public T foo(U u);
}
I want to implement this interface under the assumption that T and U are the same type. I thought maybe I could leave the type parameters in as they are, and then as long as I only ever instantiate this particular implementation with two of the same type, that it might work:
public class MyOuterClass<A> {
public class MyClass<T, U> implements MyInterface<T, U> {
#Override
public T foo(U u) {
return u; //error here
}
//even though in the only instantiation of MyClass, T and U are the same
private MyClass<A, A> myInstance = new MyClass<A, A>();
}
But, perhaps unsurprisingly, this doesn't work, as types T and U are incompatible.
So then I thought maybe I could change MyClass to specify that its types would always be the same, by changing it to something like MyClass<A, A> implements MyInterface<A, A> or similar, but I get errors saying that T is already defined.
Is there a way to implement MyClass so that its two types will be the same?
(I'm more of a C++ guy than Java, so sorry if I'm missing something fundamental about Java's generic's here.)
Your myclass needs to look like this:
public class MyClass<T> implements MyInterface<T, T> {
#Override
public T foo(T in) {
return in;
}
}
Let's review what your suggested class definition does:
public class MyClass<T, U> implements MyInterface<T, U>
In this code, T and U do two things each:
in the first occurance they define a type variable of your MyClass class
in the second occurance they specify the concrete type of the MyInterface class
Since inside the body of your class T and U are unbounded type variables (i.e. nothing is known about the actual types), they are assumed to be incompatible.
By having only a single type variable in your MyClass you make your assumption explicit: there's only a single type, and I'm using it for both types of the interface.
Last but not least: remember that the compilation of a type is complete once the source is fully handled. In other words: contrary to what C++ does, "instantiation" of a generic type ("template type" or similar in C++; Sorry for my rusty terminology) does not handle. MyClass<Foo> and MyClass<Bar> are the same type, as far as the JVM is concerned (only the compiler actually distinguishes them).
Define a single type parameter for MyClass:
class MyOuterClass<A> {
public class MyClass<T> implements MyInterface<T, T> {
public T foo(T u) {
return u;
}
}
// Need only one 'A' here.
private MyClass<A> myInstance = new MyClass<A>();
}
When you say
public class MyClass<T> implements MyInterface<T, T> {
... you are defining one generic variable for MyClass and you are saying that it fulfills both the roles T and U in MyInterface.