how to convert generics in java? - java

What is the correct way to convert generics in java?
interface A {
}
class B implements A {
}
class C {
public Set<B> returnSomeB(){
//some logic
}
}
C c = new C();
Set<A> = c.returnSomeB();
Set<A> = c.returnSomeB(); this line would give me a compile time error, what's the most proper way to seamlessly convert this since class B is a concrete class of A interface?

A variable of type Set<A> can only hold a Set<A> object, not a Set<B>, even though B is a subtype of A.
The reason is this: What if you stored a Set<B> object in a Set<A> variable, then added an object of type A (but not B) to it? It would fit all the right argument types, but the end result would be a violation of Java's type safety.
To get around this, you can use wildcards. Instead of declaring a variable of type Set<A>, declare one of type Set<? extends A>.

Try bounding your type parameter to the widest possible scope of acceptable values:
interface A {
}
interface B extends A {
}
class C {
public static void main(String[] args) {
final Set<A> foo = C.returnSomeB();
final Set<B> bar = C.returnSomeB();
}
public static <T extends A> Set<T> returnSomeB() {
return null;
}
}

Related

Issue with class cast exception between subclasses and collections in Java

So, I have the following two classes:
Class A { }
Class B extends A { }
Class C extends A { }
And the following method:
public void foo(Collection<A> bar) {
List<A> listA = new ArrayList<>();
for(A a : bar) {
//a and anotherA are both of the same subtype of A
A anotherA = getAnotherA(a);
listA.add(anotherA);
}
bar.clear();
bar.addAll(listA);
}
Now, I am trying to call this method two separate ways, but I cannot get the casting to work properly... Hopefully I am just overlooking something small.
So, here are the two ways I am calling it:
Way 1:
A obj = ...;
Field field = //get field representing a collection of sub-type of A
Collection<A> collection = field.get(...);
foo(collection);
way 2:
B obj = ...;
Set<C> setOfC = b.getSetOfC();
foo(setOfC);
I have tried numerous casting attempts, but I cannot seem to get it to compile! For instance, in way 2, I tried casting setOfC to Set<A>, but I get a class cast exception. I have tried to cast bar to Collection<? extends A>, but then bar.addAll(..) fails. I have tried to add a generic to foo, but also get errors. In way 1, I have also tried to cast collection to Collection<? extends A>, but still not luck.
You cannot pass a Set<C> into a method expecting a Collection<A>, even though a Set is a Collection, and even though a C is an A, because Java's generics are invariant.
You can introduce a type parameter on the foo method, with an A upper bound. Then you can use the type parameter throughout the method. This will ensure that the same subtype of A is used.
public static <T extends A> void foo(Collection<T> bar) {
List<T> listA = new ArrayList<>();
for(T a : bar) {
//a and anotherA are both of the same subtype of A
T anotherA = getAnotherA(a);
listA.add(anotherA);
}
bar.clear();
bar.addAll(listA);
}
Your comment seems to indicate that a and anotherA are of the same type, so this should compile for you. If not, then the getAnotherA method will need some work so that passing in a C will return a C and not an A.
if you have this classes:
Class YourA { }
Class YourB extends YourA { }
Class YourC extends YourA { }
your method's signature most be like
public <G extends YourA> void foo(Collection<G> bar) {...}

Java generic interface in collection

Can anybody explain me why I get an error only when creating B in the following code:
public class factory {
public <T> void createA(List<I<T>> c) {
A a = new A(c);//warning here
}
public <T> void createB(List<I<T>> c) {
B b = new B(c);//error here: The constructor B(List<I<T>>) is undefined
}
}
interface I<T> {
}
class B implements I<Integer> {
public B(List<I<?>> c) {
}
}
class A<T> implements I<T> {
public A(List<I<?>> c) {
}
}
B class is generic and A is not, but I have no idea why it matters in that case.
public B(List<I<?>> c) {
}
The ? is the unknown type. It is not a wild card for any other type. The compiler tells the truth, there is no constructor for the type List<I<T>> (T is not ?)
It doesn't really work for the other method too. You simply exchanged the compile error by a "unchecked conversion" warning. Because class A is parametized, you'd have to call it like that:
public <T> void createA(List<I<T>> c) {
A<T> a = new A<T>(c);
}
And voilĂ , say hello to the same error.
A is a generic class, which means that when you use A by itself, it is a raw type. When you use a raw type, you turn off all the generics for its methods and constructors. So for example, the following will compile:
A a = new A(new ArrayList<String>());
B is not a generic class, so using B by itself is not a raw type, and does not turn off generics.

Java Generic Conversion not as expected

If someone could resolve this puzzle for me. I am sure I am missing something !
interface a { }
class b implements a { }
class c extends ArrayList<b> { }
class d {
d(ArrayList<a> param) {}
}
class e {
public static void main(String[] arg) {
d newd = new d(new c());
}
}
This code has an error:
Error d(ArrayList<a>) is not applicable (actual argument c cannot be converted to ArrayList<a> by method invocation conversion)
Surely class c can be converted to ArrayList<a> as c extends ArrayList<b> and b implements a !
I have tried explicit conversion but it does not help.
It does not make sense either to change class c extends ArrayList<b> to extends ArrayList<a> as the purpose of c to to be a collection of b and the interface a is merely for display purposes. Also class d is a generic display class that relies on functionality expressed in the interface a and so makes no sense to change that either.
Advice would be handy !
This should suit your needs:
class d {
d(ArrayList<? extends a> param) {
}
}
c isn't an ArrayList<a> but an ArrayList<? extends a> since b is a subtype of a.
The problem is that an ArrayList is not the same thing as an ArrayList, even if b extends A. What would happen if D called param.add(new AImpl()), where AImpl was some other implementation of interface A?
For more details, see The Java Generics Tutorial section on inheritance.
(As an aside, it's also generally not a good idea to extend an ArrayList - most of the time, you want to be wrapping it, not extending it.)
Please respect java naming conventions:
interface A {
}
class B implements A {
}
class C extends ArrayList<B> {
}
class D {
D(ArrayList<A> param) {
}
}
class E {
public static void main(String[] arg) {
D newd = new D(new C());
}
}
So your assumption is that ArrayList<B> is castable to an ArrayList<A> as B implements A. This is not true, this is not how generics work. You simply cannot cast generic types.
Try doing something like:
final ArrayList<Object> myNewArrayList = new ArrayList<String>();
To distill the problem.
You need to use bounded type parameters to solve this. Changing your D class to:
class D {
D(ArrayList<? extends A> param) {
}
}
Would make it work as now you're saying: I want a collection of any type as long as it's an A.
It should be:
d(ArrayList<? extends a> param) {}
ArrayList is not a subclass of ArrayList because the former can have a's added to it while the latter cannot.

Java Generics: Multiple Inheritance in Bounded Type Parameters <T extends A & I>

I am about to create a factory which creates objects of a certain type T which extends a certain class A and another interface I. However, T must not be known. Here are the minimum declarations:
public class A { }
public interface I { }
This is the factory method:
public class F {
public static <T extends A & I> T newThing() { /*...*/ }
}
This compiles all fine.
When I try to use the method the following works fine:
A $a = F.newThing();
...while this does not:
I $i = F.newThing();
The compiler complains:
Bound mismatch: The generic method newThing() of type F is not applicable for the arguments (). The inferred type I&A is not a valid substitute for the bounded parameter
I can't understand why. It is clearly stated that "newThing returns something of a certain type T which does extend the class A and implement the interface I". When assigning to A everything works (since T extends A) but assigning to I does not (because of what?, clearly the thing returned is both an A and an I)
Also: When returning an object, say B of the type class B extends A implements I, I need to cast it to the return type T, although B matches the bounds:
<T extends A & I> T newThing() {
return (T) new B();
}
However, the compiler does not throw any warnings like UncheckedCast or the like.
Thus my question:
What is going wrong here?
Is there an easy away to achieve the desired behavior (i.e. assigning to a variable of static type A or I), like there is in solving the return-type-problem by casting, in the factory method?
Why does the assignment to A work, while to I does not?
--
EDIT: Here the complete code snippet which totally works using Eclipse 3.7, project set up for JDK 6:
public class F {
public static class A { }
public static interface I { }
private static class B extends A implements I { }
public static <T extends A & I> T newThing() {
return (T) new B();
}
public static void main(String... _) {
A $a = F.newThing();
// I $i = F.newThing();
}
}
EDIT: Here is a complete example with methods and invocation which does work at runtime:
public class F {
public static class A {
int methodA() {
return 7;
}
}
public static interface I {
int methodI();
}
private static class B extends A implements I {
public int methodI() {
return 12;
}
}
public static <T extends A & I> T newThing() {
return (T) new B();
}
public static void main(String... _) {
A $a = F.newThing();
// I $i = F.newThing();
System.out.println($a.methodA());
}
}
As for the second question:
Consider this case:
class B extends A implements I {}
class C extends A implements I {}
Now, the following uses type inference:
<T extends A & I> T newThing() {
return (T) new B();
}
So you could call this:
C c = F.newThing(); //T would be C here
You see that T could be anything that extends A and I you can't just return an instance of B. In the case above the cast could be written as (C)new B(). This would clearly result in an exception and thus the compiler issues a warning: Unchecked cast from B to T - unless you're supressing those warnings.
This doesn't do what you expect it to. T extends A & I indicates that the caller can specify any type that extends A and I, and you'll return it.
I think that one way to explain it is by replacing the type parameter with the actual type.
The parameterized signature of the methods is:
public static <T extends A & B> T newThing(){
return ...;
}
The <T extends A & B> is what is called a type parameter. The compiler would expect that this value is actually substituted with the actual type (called type argument) when you actually use it.
In the case of your method the actual type is decided by means of type inference. That is, <T extends A & B> should be replaced by a real existing type that extends A and implements B.
So, let's say that classes C and D both extends A and implements B, then if your signature were like this:
public static <T extends A & B> T newThing(T obj){
return obj;
}
Then, by type inference, your method would be evaluated as follows:
public static C newThing(C obj){
return obj;
}
if you invoke with newThing(new C()).
And would be as follows
public static D newThing(D obj){
return obj;
}
if you invoke with newThing(new D()).
This would compile just fine!
However, since you are not actually providing any kind of type to validate type inference in your method declaration, then the compiler could never be sure what is the actual type (type argument) of your type parameter <T extends A & B>.
You might expect that the actual type is C, but there may be thousands of different classes that satisfy that criteria. Which of those should the compiler use as the actual type of your type argument?
Let's say that C and D are two classes that extend A and implements B. Which of these two actual types should the compiler use as type argument for your method?
You could have even declared a type argument for which there is not even an existing type that you can use, like saying something that extends Serializable and Closable and Comparable and Appendable.
And perhaps there is not a class in the whole world that satisfies that.
As such, you must understand that the type parameter here is just a requirement for the compiler to validate the actual type that you use, a placeholder for the actual type; and this actual type must exist at the end and the compiler will use it to replace appearances of T. Therefore the actual type (type argument) must be inferable from the context.
Since the compiler cannot tell with certainty which is the actual type that you mean, basically because there is no way to determine that by type inference in this case, then you are forced to cast your type, to ensure the compiler that you know what you are doing.
As such, you could implement your method using type inference like this:
public static <T extends A & B> T newThing(Class<T> t) throws Exception{
return t.newInstance();
}
This way, you would be actually telling the compiler what is the actual type argument to be used.
Take into account that when the bytecodes are generated, the compiler must substitute T for a real type. There is no way to write method in Java like this
public static A & B newThing(){ return ... }
Right?
I hope I have explained myself! This is not simple to explain.
Simplest solution is create an abstract base class that extends and implements whatever class and interfaces you want and return that type. It doesn't matter that you're constraining your return type to extend this base class as you were already constraining the return type to its superclass.
eg.
class C {}
interface I {}
abstract class BaseClass extends C implements I {}
// ^-- this line should never change. All it is telling us that we have created a
// class that combines the methods of C and I, and that concrete sub classes will
// implement the abstract methods of C and I
class X extends BaseClass {}
class Y extends BaseClass {}
public class F {
public static BaseClass newThing() {
return new X();
}
public static void main(String[] args) {
C c = F.newThing();
I i = F.newThing();
}
}

Avoiding Java Type Erasure

Is there a way one could avoid type erasure and get access to a type parameter?
public class Foo<T extends Enum<?> & Bar> {
public Foo() {
// access the template class here?
// i.e. :
baz(T.class); // obviously doesn't work
}
private void baz(Class<T> qux) {
// do stuff like
T[] constants = qux.getEnumConstants();
...
}
}
I need to know about T, and do things with it. Is it possible, and if so, how can it be done without passing in the class in the constructor or anywhere besides the parameter?
EDIT: The main purpose of this question is to find out if there is any practical way around type erasure.
AFACT, there is no practical way around type erasure because you can't ask for something which the runtime doesn't have access to. Assuming of course you agree that sub-classing generic classes for each enum which implements Bar interface is a practical work around.
enum Test implements Bar {
ONE, TWO
}
class Foo<T> extends FooAbstract<Test> {
public Foo() {
ParameterizedType genericSuperclass =
(ParameterizedType) getClass().getGenericSuperclass();
baz((Class<T>) genericSuperclass.getActualTypeArguments()[0]);
}
private void baz(Class<T> qux) {
T[] constants = qux.getEnumConstants();
System.out.println(Arrays.toString(constants)); // print [ONE, TWO]
}
}
interface Bar {
}
class FooAbstract<T extends Enum<?> & Bar> {
}
If you're willing/able to hand a class token to the constructor:
public Foo(Class<T> clazz) {
baz(clazz);
}
private void baz(Class<T> qux) {
// ...
}
That way, you can produce objects of type T with Class.newInstance(), attempt to cast arbitrary objects to T using Class.cast(), etc.
What do you intend to do in baz()?
As pholser points out in his answer, the only way to achieve this is by passing in the Class object representing the type T. It's because of Type Erasure that something like T.class isn't possible because T is erased before runtime.
You seem resistant against passing in the Class object, but it's the only way to use the method getEnumConstants(). Here is a self contained example:
public class Foo<T extends Enum<?> & Bar> {
final Class<T> clazz;
public Foo(Class<T> clazz) {
this.clazz = clazz;
}
public void baz() {
T[] constants = clazz.getEnumConstants();
System.out.println(Arrays.toString(constants));
}
public static void main(String[] args) {
new Foo<MyEnum>(MyEnum.class).baz(); //prints "[A, B, C, D]"
}
}
public interface Bar { }
public enum MyEnum implements Bar { A, B, C, D; }
Use a super type token as proposed by Neil Gafter and used by libraries like guice for this purpose.
See http://gafter.blogspot.com/2006/12/super-type-tokens.html for the original description and I've check out the guice source for CA radio life working implementation.
there is another q which has an answer with worked example inline here How can I pass a Class as parameter and return a generic collection in Java?
In some cases you can use a workaround suggested by Richard Gomes.
When creating instances of anonymous classes, the type parameter class info is available.
class A<T>
{
A()
{
java.lang.reflect.ParameterizedType parameterizedType = (java.lang.reflect.ParameterizedType) (this.getClass().getGenericSuperclass());
System.out.println(parameterizedType.getActualTypeArguments()[0]);
}
}
public class Example {
public static void main(String[] args) {
A<String> anonymous = new A<String>() {}; // prints java.lang.String
}
}
Note that multiple instances created this way will be of different anonymous classes, and if that's a problem you might want a class A_String_Factory with a createNew() function based on clone to replace the calls to new.

Categories

Resources