List of abstract class containing extended classes - java

So my problem is this. I am using java and I am trying to do something like this (I am working offline so it is harder for me to give code examples but if needed I will) :
Class A - abstract class
Class B - abstract class with list<A> as property
Class C - extends class A
Class D - extends class B
and in this class, in the constructor I am trying to create the list which is in the B properties with a new object from class C.
The error is: Type mismatch: cannot convert from C to A.
I can't seem to make it work. Any ideas why?
Edit:
abstract public class A {
public int theInt;
}
abstract public class B {
public List<A> theList;
}
abstract public class C extends A {
}
abstract public class D extends B {
public D(){
this.theList = new ArrayList<C>();
}
}
This is the code and I have a compiliation error like I mentioned.

Initialize it as
List<A> list= new ArrayList<A>();
That way you would be able to add any subtype of A
If you want to assign ArrayList<C> to as it appears from the comment
Do it this way
list.AddAll(arrayListC);

ArrayList<C> does not extend List<A>. Therefore the assignment
this.theList = new ArrayList<C>();
is invalid.
You can fix this by adding a type parameter to B
abstract public class B<T extends A> {
public List<T> theList
}
abstract public class D extends B<C> {
public D(){
this.theList = new ArrayList<C>();
}
}
You could even add a type parameter to D:
abstract public class D<T extends A> extends B<T> {
public D(){
this.theList = new ArrayList<T>();
}
}
Alternative:
Just use ArrayList<A> instead of ArrayList<C>.
Please note that lst.add(x); will not compile for the following type combinations:
type of lst | type of x
============================|================================
List<? extends A> | A
----------------------------|--------------------------------
List<C> | E extends A but not C
----------------------------|--------------------------------
List<? extends A> | E extends A

Related

Java Generics create list of objects which extends class A and implements interface B

Consider this scenario
public abstract class A{
}
public interface B{
}
How would I create a List of objects which extend A and implement B?
List<? extends A implements B> list = new List();
Thanks
You can also use '&' operator and declare the unknown class as a type parameter like below. The advantage of this vs declaring a class XXX extends B implement A is that your code will work with any such a class that satisfy the constraints not just XXX descendants.
import java.util.*;
interface A {};
class B {};
class Test {
public <T extends B & A> List<T> asList(Collection<T> elements) {
List<T> result = new ArrayList<>(elements.size());
for (T element : elements) result.add(element);
return result;
}
}
As i know, this is not supported with wildcard generics. This can be done with Type generics.
public abstract class A{}
public interface B{}
public class Match extends A implements B{}
public class NotMatch1 implements B{}
public class NotMatch2 extends A{}
public class MyChecketList<T extends A&B> extends ArrayList<T>{}
public void use(){
List<Match> a = new MyChecketList<>();//valid
List<NotMatch1> b = new MyChecketList<>();//compiler error
List<NotMatch2> c = new MyChecketList<>();//compiler error
MyChecketList<Match> d = new MyChecketList<>();//valid
MyChecketList<NotMatch1> e = new MyChecketList<>();//compiler error
MyChecketList<NotMatch2> f = new MyChecketList<>();//compiler error
}

How to infer the generic type to avoid unchecked casting

Suppose we have a class A that has two subclasses A1, A2.
Suppose we have another class B, that also has two subclasses, B1 an B2:
class B{
List<? extends A> myGenericList;
B(List<? extends A> myGenericList){
this.myGenericList = myGenericList;
}
public List<? extends A> getMyGenericList(){
return myGenericList;
}
}
class B1 extends B{
B1(List<A1> a1List){
super(a1List);
}
}
class B2 extends B{
B2(List<A2> a2List){
super(a2List);
}
}
Now, if we have a class C1 like this:
class C1{
...
public void doSomethingWithB1(B1 b1){
List<A1> a1list = (List<A1>)b1.getMyGenericList();
}
...
}
How can I implement getMyGenericList of class B so I can avoid the unchecked casting warning?
I tried something like this:
public <T extends A> List<T> getMyGenericList() {
return this.myGenericList;
}
but the compiler complains with cannot convert from List<capture#3-of ? extends A> to List<T>
Is there any way to do it?
Thanks in advance.
With the current way you've defined the B class, the myGenericList instance variable could hold a List of any subtype of A, so the unchecked cast warning you get when casting to List<A1> is justified. It could be a List<A> or a List<A2>.
If you don't really care which A you get in the list back, you can just assign it to a List<? extends A>.
List<? extends A> a1list = b1.getMyGenericList();
But if you really want to get a List<A1> back from a B1, then generics on the B class is your answer. Define T at the class level with an upper bound of A. Use it throughout your class to replace your wildcards.
class B<T extends A>
{
List<T> myGenericList;
B(List<T> myGenericList){
this.myGenericList = myGenericList;
}
public List<T> getMyGenericList(){
return myGenericList;
}
}
Your subclasses of B will define what T is respectively.
class B1 extends B<A1> // rest of class is the same
class B2 extends B<A2> // rest of class is the same
This way you have eliminated the unchecked cast warning and even the need to cast at all.
List<A1> a1list = b1.getMyGenericList();
You should move your generic definition to class level as following:
class B<T extends A> {
List<T> myGenericList;
B(List<T> myGenericList){
this.myGenericList = myGenericList;
}
public List<T> getMyGenericList(){
return myGenericList;
}
}
Now the subclass is defined with concrete parameter A1:
class B1 extends B<A1>{
B1(List<A1> a1List){
super(a1List);
}
}

How to use wildcard generics with Sets.powerSet

I have some code like this
import com.google.common.collect.Sets;
public void handleInput(Set<Object> conditions){
Set<Set<Object>> powerSet = Sets.powerSet(conditions);
...
}
This works fine. But I want to do this:
public void handleInput(Set<? extends Object> conditions){
Set<Set<? extends Object>> powerSet = Sets.powerSet(conditions);
...
}
so I can get the powerset of objects that are subclasses of object. But this won't compile and I get the error:
Type mismatch: cannot convert from Set<Set<capture#1-of
? extends Object>> to Set<Set<? extends Object>>
How can I achieve this goal?
EDIT: I guess it has something to do with the generic type getting erased at compile time, so that the compiler can't know that powerSet won't add something illegal to the sets it's creating. I've reworked the client, by casting all the inputs to Object, and removing the wildcard altogether. Is this the best way? Thanks!
In this case it doesn't make any sense - since all Java classes extend java.lang.Object at some point.
So ? extends Object is redundant.
But speaking of Sets.powerSet, this works like a charm:
public class TestClass {
public static class A {}
public static class B extends A {}
public static class C extends B {}
public Set<? extends Set<? extends A>> exampleMethod(Set<? extends A> input) {
return Sets.powerSet(input);
}
public static void main(String[] args) {
final TestClass testClass = new TestClass();
final A a = new A();
final B b = new B();
final C c = new C();
System.out.println(
testClass.exampleMethod(
ImmutableSet.of(a, b, c)
)
);
}
}
as #slnowak notes, when you are extending Object, the code is really redundant.
However, to understand the Exception and avoid it...
public void handleInput(Set<? extends Object> conditions){
Set<? extends Set<? extends Object>> powerSet = Sets.powerSet(conditions);
...
}
this will compile and, more usefully, you can restrict the types in your conditions argument using this method, for instance - you could have:
public void handleInput(Set<? extends Number> conditions){
Set<? extends Set<? extends Number>> powerSet = Sets.powerSet(conditions);
...
}
and this would prevent you passing in sets that had non-numeric types and warn you of this at compile time.

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.

inheritance & collections related compile error

I'm trying to figure out why this code won't compile.
I have interface A extended by interface B.
Class C which implements interface B.
When I call a method that takes in a single object of type A, I can pass in an object of type C and it's fine.
When I call a method that takes in a java.util.List of type A, I cannot pass in a java.util.List of objects of type C. Eclipse generates the following error:
The method addAList(List) in the type Test1 is not applicable for the arguments (List)
Source code example is below.
import java.util.ArrayList;
import java.util.List;
public class Test1 {
public void addASingle(A a) {
return;
}
public void addAList(List<A> aList) {
return;
}
// **********************************
public static void main(String[] args) {
Test1 t = new Test1();
C c1 = new C();
List<C> cList = new ArrayList<C>();
cList.add(c1);
t.addASingle(c1); // allowed
t.addAList(cList); // The method addAList(List<Test1.A>)
// in the type Test1 is not applicable for the arguments (List<Test1.C>)
}
// **********************************
public static interface A {
}
public static interface B extends A {
}
public static class C implements B {
}
}
A List<Car> is not a List<Vehicle>. If it was, you could do the following:
List<Car> cars = new ArrayList<>();
List<Vehicle> vehicles = cars;
vehicles.add(new Bicycle());
and you would end up with a list of cars which contains a bicycle. It would ruin the type-safety of generic collections.
You probably should used a List<? extends A> instead of List<A>. List<? extends A> means: a List<some class which is A or which extends A>.
It expects List and you are passing List,
Change it to
public void addAList(List<? extends A> aList) {
return;
}
it expects List of type A....write it in your method signature.
public void addAList(List<? extends A> aList) {
return;
}
by writing this you declare that..your method expects any List which contains any subtype of A...This is called wildcard.

Categories

Resources