Isn't it impossible to extend a generic type with an undefined type parameter ex:
class Foo extends enum<E>
How do they extend it?
edit: also where is the values() method defined?
Thanks in advance
It is indeed illegal to extend a generic type with an undefined type parameter. However, enums don't do that. If you're decompiling some java code and saw a <E> there (And your enum type is not named E), your decompiler isn't processing generics properly.
An enum implicitly extends Enum<YourEnumType>. That is, implicitly the compiler generates a class YourEnumType extends Enum<YourEnumType>. By passing down its own type, it allows Enum's compareTo and valueOf functions to reject values from different types of enums.
Related
public class MyClass<T> extends T {...}
The above declaration will fail to compile with the error:
error: unexpected type
class MyClass<T> extends T {}
^
required: class
found: type parameter T
where T is a type-variable:
T declared in class MyClass
I can't really think of a reason for this to happen, so I am wondering if someone can shed some light on why it is that Java won't let you inherit from a generic type-variable.
The most obvious reason I can think of isn't even about type-erasure; it is the fact that when you make A a subclass of B, the Java compiler needs to know what constructors B has. If a class does not have a no-arguments constructor, then its sub-classes must call one of its defined constructors. But when you declare
public class MyClass<T> extends T {...}
It is absolutely impossible for the compiler to know what the super constructors are, since T is not fixed at compile-time (that's the whole point of generics after all), which is a situation that cannot be allowed in Java.
Java has quite a lot of language restrictions unlike C++ for example. What you want is not possible for many reasons listed in the comments (T might be final or have abstract methods). However, you are allowed to extend from a supertype having the generic type parameter:
public class MyClass<T> extends AnotherClass<T>
You might find the following alternative interesting:
public class MyClass<T extends AnotherClass> extends AnotherClass
What you want to do does make not much sense.
Your question is not so weird as it may look like :) Consider how would you deal with following:
Suppose your real class for T has a single constructor with 3 parameters. How would you implement the constructor of inherited class, if you don't know how to call the super constructor?
Suppose your real class for T has public final methods and you have defined methods with the same signature in the inherited class. What method would your object have? You cannot resolve such conflict.
Simple deductions based on your question.
T is a type.
MyClass extends T - MyClass is enhanced version of T.
MyClass < T> extends T - MyClass is enhanced T, but only for Type T.
there is no reason to state ' I extend T but only for type T'.
If you extend T, MyClass is already a Type of T & definitely not some X,Y or Z.
Generics are needed if you want to ensure Type safety, if you extend it is already type safe.
Take for instance, the method below.
<T extends MyInterface> void myFunction(Class<T>... types) {
// Do some stuff that requires T to implement MyInterface
}
Now, given the method call below assuming MyClass1 and MyClass2 both implement MyInterface.
myFunction(MyClass1.class, MyClass2.class)
I get the following error.
Incompatible equality constraint: MyClass1 and MyClass2
How do I make this work? More specifically, how would one use a variadic parameter of class types implementing an interface in Java?
You've declared T to have an upper bound of MyInterface. By passing in MyClass1.class and MyClass2.class, the compiler must infer MyInterface for T.
However, the type of the parameter type is Class<T>..., restricting what is passed in to MyInterface.class and no subtypes.
Depending on the "stuff" you're doing, you can place a wildcard upper bound on the type of types to get it to compile.
<T extends MyInterface> void myFunction(Class<? extends T>... types) {
Having a T means that T has one fixed value, which means that all of the Class<T>... parameters must be the exact same type. The compiler cannot infer T==MyInferface because Class<MyClass1> is not a subclass of Class<MyInterface>.
You want to allow each parameter to have a different type. That requires a different signature:
void myFunction(Class<? extends MyInterface>... types)
There's no need for T at all.
With the first parameter, the type variable T is set to MyClass1.
MyClass1 should implement the interface, so the constraint extends MyInterface is fulfilled.
Of course a Class<MyClass2> is not a Class<MyClass1>.
And that's why you get the error.
Example:
interface S {}
interface SS extends S {}
abstract class A<T extends S> {
T get() {…}
}
abstract class B<BT extends SS> extends A<BT> {}
Why does ((B)someInstanceOfB).get() return an object of type S (and we should cast it to SS manually), when the compiler could determine that returned object is at least of type SS?
Why doesn't the compiler make implicit class cast to have clearer code? The code is of version 1.5+ and this is not secret for compiler. (Solved)
Update: Why doesn't the compiler compile B class as it implicitly has method BT get() { return super.get(); } ?
Is this problem solved in Java 1.7+?
By casting to B you're using a raw type.
Using a raw type removes all generic information from the object, no matter where it is.
That means that for the raw type B the get method looks like it is returning a S (because that's the erasure (i.e. the actual type used at runtime) of the type parameter T).
To avoid this, never use raw types! They are meant exclusively for backwards compatibility and interaction with legacy code.
Cast to B<? extends SS> instead.
And no: this problem will probably never be "solved" in any future version of Java, as it's non-existing when you use generics correctly.
Regarding Update: no, B does not have a method BT get(). It has a method T get() where T is bound to the type parameter BT which has a lower bound SS. But since all generic type information is discarded when you use a raw type, T will still fall back to the original erasure, which is S.
The rule is quite simple and basically says "when you use a raw type it acts as if the class has no generics at all". The implications, however, are not always obvious, as in this case.
You didn't parameterize your instance of B.
Let's say you have the class C implementing SS:
Then, new B<C>().get(); would return an object of type C.
On the new B().get(); line, your IDE must have told you "B is a raw type. References to generic type B should be parameterized.".
This may seem a dumb question (as it could be very project specific) but in this case it's general enough:
I'm running a piece of code which gives the ClassCastException, with the error message I wrote as title. Here the mentioned cast is from an object of type java.lang.reflect.Type to Class<?>.
Apart from primitive types, which type in Java is not a class?
Thanks for your answers
A Class is a Type but a Type isn't necessary a Class, since Class implements Type.
Basically, your myType could also be any of the implementing classes of the interfaces GenericArrayType, ParameterizedType, TypeVariable or WildcardType, since they all extend the Type interface.
Try to print myType.getClass() to see the type you mean: you'll get Class<TheSaidType>.
From the Javadoc for Type you can see it has a number of implementing types.
All Known Subinterfaces:
GenericArrayType, ParameterizedType, TypeVariable<D>, WildcardType
All Known Implementing Classes:
Class
Th sub-interfaces are implemented using internal data structure classes.
Have a base genric class like ClassBase<T>
I found I can use
ClassDerived extends ClassBase
or
ClassDerived<T> extends Classbase<T>
So basically it means I can remove generics in the derived class, is that right?
Since all generic type information is erased in the compilation process, of course you can do that. However, most compilers will generate a warning unless the warning is turned off or suppressed. For instance, if I write class Foo extends HashMap {}, Eclipse reports: "HashMap is a raw type. References to generic type HashMap should be parameterized"
You can extend the raw version of a generic class, that's correct. Of course your second example:
ClassDerived<T> extends Classbase
... actually re-introduces a type parameter.