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.
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.
I have created an interface called Identifier. And a class which implements Identifier interface is MasterEntity bean like below.
public class MasterEntity implements Identifier{
}
In DAO method, I wrote a method like below method signature.
public Identifier getEntity(Class<Identifier> classInstance){
}
Now from Service I want to call the method like below.
dao.getEntity(MasterEntity.class);
But am getting compilation error saying -
The method getEntity(Class<Identifiable>) in the type ServiceAccessObject
is not applicable for the arguments (Class<MasterEntity>)
I know if I use like below it will work.
public Identifier getEntity(Class classInstance){
But by specifying the Class of type Identifiable, how it can be done?.
Change DAO method signature to
public Identifier getEntity(Class<? extends Identifier> classInstance)
By declaring method as you described above you specify that the only applicable argument is Identifier.class itself. To be able to accept Identifier.class implementations, you should define generic type bounds.
List<? extends Shape> is an example of a bounded wildcard. The ? stands for an unknown type, just like the wildcards we saw earlier. However, in this case, we know that this unknown type is in fact a subtype of Shape. (Note: It could be Shape itself, or some subclass; it need not literally extend Shape.) We say that Shape is the upper bound of the wildcard.
Also you can take a look at this answer: https://stackoverflow.com/a/897973/1782379
The following types do not produce any compiler warnings:
public abstract Class<? extends BaseType> getSubclassType();
#Override
public Class<SubType> getSubclassType() {
return SubType.class;
}
The following concrete method produces an unchecked cast compiler warning:
public abstract <T extends BaseType> Class<T> getSubclassType();
#Override
public <T extends BaseType> Class<T> getSubclassType() {
return (Class<T>) SubType.class;
}
Is there any way to define the method with a generic type parameter (in the base class) and return the appropriate concrete type in the subclass without generating compiler warnings? Obviously, both examples that I've given above will work just fine, I just want to know if there is a more correct way to define the method. Note: I do know that I could do the following:
public abstract class BaseClass <T extends BaseType>
public abstract Class<T> getSubclassType();
I am wondering if there is a way to define the method header itself, with a generic type, without defining it on the class. And also, I am curious why the wildcard declaration does not give and warnings but the "equivalent" generic declaration does.
The two are very different.
In the first one, the caller does not know what the type argument of the Class that is returned is. The caller cannot make any assumptions about what that type argument is, other than it extends BaseType. The method can choose to return a Class whose type argument is any subtype of BaseType it wants, unknown to the caller.
In the second case, the method is a generic method and T is a type variable of the method. That means that the caller can choose T to be whatever it wants, and expect the method to magically work with T being that type. Notably, the method does not receive any arguments, which means the method does not have any information at runtime regarding what T is at all, and must nevertheless somehow figure out how to return a Class<T>. Obviously, this is impossible, unless it always returns null.
If I, for example, have a method that uses varargs for Class types that extends a super class like
this:
public static <E extends Example> void test(Class<E>... es){}
Then I try to call that method with two different subclasses of Example, I can only do so if I make an array with the two classes in it.
//this does not work
test(E1.class,E2.class);
//this does work
test(new Class[]{E1.class,E2.class});
public class E1 extends Example {}
public class E2 extends Example {}
Why is this?
This line doesn't compile:
test(E1.class,E2.class);
There is only one type parameter E, and Java must match the inferred types of the arguments exactly. It cannot infer Example, because the objects are Class<E1> and Class<E2>, not Class<Example>. The invariance of Java generics prevents this from happening.
You can work around this by introducing an upper bound wildcard on test's generic type parameter:
public static <E extends Example> void test(Class<? extends E>... es)
This allows Java to infer Example for E, by satisfying the upper bound wildcard with E1 and E2.
The second line creates a raw array of Classes, bypassing generics and generating an "unchecked call" warning.
new Class[]{E1.class,E2.class}
If you were to attempt to provide a type argument to Class here, you would get a compiler error with any half-way reasonable type parameter:
// Needs Class<Example> but found Class<E1> and Class<E2>
test(new Class<Example>[]{E1.class,E2.class});
// Needs Class<E1> but found Class<E2>
test(new Class<E1>[]{E1.class,E2.class});
// Needs Class<E2> but found Class<E1>
test(new Class<E2>[]{E1.class,E2.class});
Satisfying the inference by using a wildcard here just uncovers the real problem here -- generic array creation.
// Generic array creation
test(new Class<? extends Example>[]{E1.class,E2.class});
You define the generic E of a single class that extends Example. You can't reference two different classes in your call because it won't know what type E is. It expects only one type.
Although this does not work:
test(E1.class, E2.class);
This does:
test(E1.class, E1.class);
The reason you can do it with an array is because of type erasure. The compiler doesn't see that the classes within the array are different.
If you change your method to accept any class that extends Example it will work.
public static void test(Class<? extends Example>...classes)
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.