Varargs of type Class in Java - java

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)

Related

Variadic Parameter of Class Types - Java

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.

What is the purpose of generics before return type

For example, in the following code
public <U extends Number> void inspect(U u){
// Some method
}
what is the purpose of (what is this line doing or how is it read)
that comes just before the return type
This is the syntax that makes your method (as opposed to your class) generic.
Methods of regular and generic classes can be made generic on their own type parameters. In this case, your inspect method is generic on U, which must be a type extending from Number.

Why is this type parameter preserved in the bytecode?

The type erasure page says that
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
However, for the following class:
public class Foo<E extends CharSequence> {
public E something;
}
javap -c Foo prints:
public class Foo<E extends java.lang.CharSequence> {
public E something;
}
Why is the type parameter not replaced with the bound (CharSequence), but is preserved as E?
What you printed isn't bytecode. It is the method signature. It's put there so the compiler can enforce typesafety when compiling other classes that call it.
Type information is preserved on classes and methods, but not on actual fields. If you wrote
class Foo extends Bar<String> {
}
...you could extract Bar<String> at runtime, but if you had
new Bar<String>();
...you could not extract Bar<String> there.
I agree with #user207421 answer. One can differentiate between raw types and complete generic type by trying out the following code:
public class App {
public static void main(String args[]) {
Foo raw = new Foo(Something);
}
}
When you check the bytecode for this class, you will find that E is missing. So this is a raw type. These raw types are not bounded and may even require casting due to which Exceptions might be thrown. That is why Generics are used; to ensure type safety. It is a complete source code mechanism.
The type parameter is preserved because it must be known for sub-classing.
Definitions
public class Foo<E extends CharSequence>
and
public class Foo<CharSequence>
are NOT equal, since latter would not allow sub-class to be declared as, say:
public class MyStringFoo extends Foo<String> { }
whereas former does.

Multiple bounds on type with method invocation conversion

Consider this small test class:
import java.util.List;
public abstract class Test {
// CAN modify this constructor interface
public <T extends Runnable & Comparable<T>> Test(List<T> l) {
setList((List<Runnable>)l); // <-- (a) warning
setList(l); // <-- (b) error
}
// CANNOT modify this interface
public abstract void setList(List<Runnable> l);
}
This succinctly represents my problem, in that I want to use generic methods which take objects of type T (which are both Runnable and Comparable<T>) like Test's constructor, but I am constrained to using other methods with interfaces like that of setList to take collections of T.
(a) Why does the compiler warn about an unchecked conversion to List<Runnable> when l is an instance of List<T> where T extends, and has type erasure, of Runnable (as per JLS SE7 §4.6)?
(b) The compiler raises the following error:
error: method setList in class Test cannot be applied to given types;
setList(l); // <-- compiler error
^
required: List<Runnable>
found: List<T> reason: actual argument List<T> cannot be converted to
List<Runnable> by method invocation conversion
where T is a type-variable:
T extends Runnable,Comparable<T> declared in constructor <T>Test(List<T>)
My understanding here is that method invocation conversion couldn't convert T to Runnable because perhaps this is considered a narrowing operation, but this is counter-intuitive, as I expected it could as T intersects both Runnable and Comparable<T>.
Must I resort to unchecked conversions (a) of instances of T to List<Runnable> in this case?
EDIT
The answer turns out to be yes, as Bhesh has pointed out below, generic types are invariant in Java. If I could use the following, error (b) would not occur:
public abstract void setList(List<? extends Runnable> l);
For more information, see this excellent tutorial and this section on capture conversion in the JLS SE7 §5.1.10.
a) T is-a Runnable but List<T> is-not-a List<Runnable>. Generic types are invariant in Java.
b) Reason is the same as the above. Method parameters are also invariant in Java. (Note: The method arguments are covariant.)
The cast in setList((List<Runnable>)l); should be safe, because we know that T is always going to be something that implements Runnable. This is the type of situation for which the annotation #SuppressWarnings("unchecked") exists.
Still a problem could be (depending upon your situation) that you are invoking an overridable method from a constructor.

<T extends AnInterface> vs <? extends AnInterface>

I am a little confused with something.
I have a class where its not a collection, but it does refer to generic objects:
public class XClass<E extends AnInterface>{
E instanceobject;
public void add(E toAdd){}
}
public interface AnInterface{}
public class A implements AnInterface{}
public class B implements AnInterface{}
I believe I read somewhere that <? extends AnInterface> is to be used (when declaring an instance of XClass) if you want multiple subtype-types in the generic object at the same time, whereas <T extends AnInterface> would only allow you to have a single type of subtype in the generic class at once?
However, I can just use:
XClass<AnInterface> xc = new XClass<AnInterface>();
A a = new A();
B b = new B();
xc.add(a);
xc.add(b);
and this way I can pass in multiple subtypes of Supertype to the generic class......
I am not seeing the purpose of using "?" and is there anything wrong with using the Interface as the generic parameter?
The reason why you can add objects of both type A and B is due to the fact that you parametized your XClass with the interface, so there is nothing wrong with adding two different classes that implement that interface.
If, on the other hand, you had defined XClass as:
XClass<A> xc = new XClass<A>();
then the expression xc.add(b); would give a compilation error, since all the objects added must have the same type as was declared, in this case, A.
If you declare you xc as, for instance:
XClass<? extends AnInterface> xc = new XClass<AnInterface>();
Then it's not legal anymore to add a or b, since the only thing we know is that xc is of some unknown but fixed subtype of AnInterface, and there is no way to know if that unknown type is A or B or anything else.
But let's say you're writing a method to accept a XClass type that you can iterate over the elements that were added before. Your only restriction (for the sake of the example), is that the items extend AnInterface, you don't care what the actual type is.
You can declare this method like:
public static void dummyMethod(XClass<? extends AnInterface> dummy){
//do stuff here, all the elements extend (implement in this case), AnInterface, go wild.
}
And now you can pass into this method anything like XClass<A>, XClass<B> or XClass<AnInterface>, and it will all be valid.
Keep in mind that you can't add to the object you pass, for the same reason above. We don't know what the unknown type is!
public static void dummyMethod(XClass<? extends AnInterface> dummy){
//do stuff here, all the elements extend (implement in this case), AnInterface, go wild.
dummy.add(new A()); //you can't do this, we have no idea what type ? stand for in this case
}
You can use E if you want to have an instance of XClass to use only one subclass of AnInterface and no other Classes implementing AnInterface that do not extend / implement E.
For example given
public class ClassOne implements AnInterface {} and public class ClassTwo implements AnInterface {}
If you were to use
public class XClass<E extends AnInterface> and <ClassOne>XClass xc = new <ClassOne>XClass() then you can only use an object of ClassOne in your add method not one of ClassTwo. Using ? would allow you to pass in any class implementing AnInterface, either ClassOne or ClassTwo.
Using Identifier E means "For this object I want to use type E and any subclasses", using ? means "I want to use any type that matches the the expression"
In your example you need type erasure in the method "add", so you should't use wildcards in your class.
Wildcards are only to be used when you do not need type erasure (i.e. you don't care about the type as long as it is a subclass of..) and also when you will need to subtype the generics itself.
The wildcard simply means that it will be some class that meets that criteria. So ? extends AnInterface means it will be one (and only one) class that extends AnInterface.
So it could be:
XClass<Impl1>
XClass<Impl2>
etc...
However, at runtime, you don't know what that class will be. For this reason calling methods which take the actual type as a parameter is inherently unsafe, since it's impossible for the compiler to know if the parameter is appropriate for the actual instantiated instance.
Take lists as an example. Something might be declared like this:
List<? extends Number> list = new ArrayList<Integer>();
What would happen if you try to do either of these:
list.add(new Double(0));
list.add((Number) new Long(1L));
It would not compile, because the generic parameter type is unknown at compile time. So the compiler can't tell if Double or Number would be appropriate to pass to the actual instance (in this case ArrayList<Integer>). This is when you get the infamous capture-of compile error.
This, however is permissible, since you know for certain at compile time that the list can take any instance of Number (which includes subclasses).
List<Number> list = new ArrayList<Number>();
list.add(new Double(0));
list.add((Number) new Long(1L));

Categories

Resources