How does Subclassing for Lists work? - java

I asked a similiar question 10 minutes ago, but pasted the wrong code snippet. I'm really sorry about that.
I'm currently facing an issue with base and subclasses.
While having a single object as parameter (method single) the compiler doesn't complain.
But if it comes to lists the compiler forces me to declare the list as <? extends Base>
After that I'm no longer allowed to add objects of the base type to that list.
The error message: "The method list(List<Generics.Base>) in the type Generics.C is not applicable for the arguments (List<Generics.Sub>)"
public class Generics {
class Base { }
class Sub extends Base{ }
interface I {
public void list( List<Base> list );
public void single( Base list );
}
class C implements I {
public void list( List<Base> b) { }
public void single( Base p) { }
}
void test() {
C c = new C();
c.single( new Sub() );
List<Sub> b = new ArrayList<Sub>();
c.list( b ); // error message as above
}
public static void main( String[] args) {
Generics g = new Generics();
g.test();
}
}
Is there any other way but declaring the list-methods argument as type <? extends Base> ?

Below are the 2 ways to do it....
public void list(List<? extends Base> list){
}
Or
public <T extends Base> void list(List<T> list){
}

Related

How to subclass generics properly?

I have searched other questions/answers, an I'm still confused about this JAVA ISSUE.
If I want to subclass a type that takes generic parameters, how would I do so while using generic parameters for the subclass too without it getting shadowed?
For example:
public class A <T> extends ArrayList<T> {}
So if i instantiate my custom class, with say, Integers as the parameter, does it take that value as parameter for <T> for the ArrayList part of it too? If not, then how would I specify the type for the ArrayList?
And I know sub classing containers may not be the best idea in many situations, but in this case I have decided it would be appropriate.
yes that would be how one goes about it.
public static void main(String[] args) {
B<Integer> b = new B<Integer>();
b.a = 1;
b.b = "one";
b.add(1);
b.add(2);
}
public static class A<T> extends ArrayList<T> {
public T a;
}
public static class B<T> extends A<T> {
public T b;
}
if you like you could even have them have different types, as long as you supply the super-class with it's type as well:
public static void main(String[] args) {
B<Integer, String> b = new B<Integer, String>();
b.a = 1;
b.b = "one";
b.add(1);
b.add(2);
}
public static class A<T> extends ArrayList<T> {
public T a;
}
public static class B<U, T> extends A<U> {
public T b;
}

Java generic class constructor invocation

I have following code:
public class A {}
public class B extends A {}
public class C <T extends A> {
private final T data;
public C(final T data) {
this.data = data;
}
}
public class D<T extends B> extends C<T> {
public D(T data) {
super(data);
}
public D() {
this(new B());
}
public static D<B> create() {
return new D(new B());
}
}
There is a compile error in the class D:
error: no suitable constructor found for D(B)
this(new B());
constructor D.D() is not applicable
(actual and formal argument lists differ in length)
constructor D.D(T) is not applicable
(actual argument B cannot be converted to T by method invocation conversion)
where T is a type-variable:
T extends B declared in class D
What is confusing me is the fact, that the static method D.create() that does basically the same is compiled without any errors. Can anyone explain this error? And the difference between D() and D.create()?
The error is there because for class D it is not known that the type will be B, only that the generic type will extend B - you have assumed it's going to be B because there are no other classes (yet) in your class hierarchy (a fact that the compiler must consider may change in the future).
Note that in the factory method you are instantiating the raw type for D (one without a generic parameter). Instead, provide a type:
You should change:
public static D<B> create() {
return new D(new B());
}
to:
public static D<B> create() {
return new D<B>(new B()); // Note: Added generic parameter <B>
}
Because generic type T of class D is not bound
This will work
public class E extends D<B> {
public E() {
super(new B()); // call to D's constructor public D(T data)
}
}
normally you would call the constructor of D in this way:
new D<B>(new B());
but you CAN NOT do this
public D() {
this<B>(new B());
}
Another example.
Change the code a little bit and you will see the problem.
class BBB extends B {
}
class C<T extends A> {
protected final T data;
public C(final T data) {
this.data = data;
}
}
class D<T extends B> extends C<T> {
public D() {
this(new B());
}
public T getData(){
return data;
}
}
D<BBB> dOfBBB = new D<BBB>();
BBB data = dOfBBB.getData(); // So if this(new B()) would work
// how can the data then be returned?
// Because BBB is returned but it would be
// initialized with only a B instance

How can I have instances of Base and Sub-Classes in one List?

I'm currently facing an issue with base and subclasses.
While having a single object as parameter (method single) the compiler doesn't complain.
But if it comes to lists the compiler forces me to declare the list as <? extends Base>
After that I'm no longer allowed to add objects of the base type to that list.
How can I use both types (Base and Subclass) in one list?
public class Generics {
class Base { }
class Sub extends Base{ }
interface I {
public void list( List<Sub> list );
public void single( Sub p);
}
class C implements I {
public void list( List<Sub> list) { }
public void single( Sub p) { }
}
void test() {
C c = new C();
c.single( new Sub() );
c.list( new ArrayList<Base>() ); // The method list(List<Generics.Sub>) in the type Generics.C is not applicable for the arguments (ArrayList<Generics.Base>)
}
public static void main( String[] args) {
Generics g = new Generics();
g.test();
}
}
Change:
public void list(List<Sub> list);
to:
public void list(List<? extends Base> list);
Using just List<Base> will give you compiler errors like this one:
public static void main(String[] args) {
List<Sub> subs = new ArrayList<Sub>();
doSomethingWith(subs); // The method doSomethingWith(List<Base>) in the type Main is not applicable for the arguments (List<Sub>)
}
private static void doSomethingWith(List<Base> bases) {
// Do something with bases
}
If all you're going to pass is List<Base> to doSomethingWith, then this point is moot, since this won't give you a compiler error. If you want to pass lists that are of a specific type (such as List<Sub> above), then you need to change doSomethingWith to:
private static void doSomethingWith(List<? extends Base> bases) {
This fixes the problem. You could also do it at the caller lever (but it's a bit messier):
List<Sub> subs = new ArrayList<Sub>();
doSomethingWith(new ArrayList<Base>(subs));
One issue with the wildcard (?) approach is that you can't add new items to the list. To do that, you need something like:
private static <B extends Base> void doSomethingWith(List<B> bases) {
And then add only B instances to bases.
Just declare all your lists as
List<Base> list;
Then you can add both Base objects and objects of any subclass.
Below are the 2 ways to do it....
public void inTake(List<? extends Base> list){
}
Or
public T extends Base void inTake(List<T> list){
}

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.

A way to create matched pairs of generic classes without warnings?

I wonder if there is a way to eliminate the rawtypes warning when I use generic classes that refer to each other:
public class DummyDeleteMe {
abstract class RightSide<L extends LeftSide>{ //Can I use a type parameter here?
L getLeftSide() {return _mate;}
void setLeftSide(L mate) {_mate = mate;}
L _mate;
void connectToMate(){
getLeftSide().setRightSide(this);//warning:
/** [unchecked] unchecked call to setRightSide(R) as a member of the raw type DummyDeleteMe.LeftSide
where R is a type-variable: * **/
}
}
abstract class LeftSide<R extends RightSide>{// And here?
R getRightSide(){return _mate;}
void setRightSide(R mate) {_mate = mate;}
R _mate;
}
class RightSideSub extends RightSide<LeftSideSub>{
void connectToMate(){
getLeftSide().setRightSide(this);//No warning
}
}
class LeftSideSub extends LeftSide<RightSideSub>{}
}
The compiler warning is because the LeftSide in the parameter bound is a raw type. Replacing it with LeftSide<?> causes an error in connectToMate. Overriding connectToMate in RightSideSub, the same code does not generate a warning.
I guess I'm looking for some kind of second type parameter, that is self referential:
abstract class RightSide<R extends RightSide<R,L>, L extends LeftSide<L,R>>{}
But that causes other type mis-match errors in the R variables and methods that return R.
I know this may not be exactly what you are looking for, but it may solve your problem. Based only on what you've shown in your code sample, there is no need for a generic at all. It's over complicating the issue. That is, if your example is a fairly close match for what you need.
Assuming it is, how about something like this.
public interface Half{
public Half getOtherHalf();
}
public class RightSide implements Half{
private LeftSide leftSide;
public void setLeftSide(LeftSide leftSide){
this.leftSide = leftSide;
}
#Override
public LeftSide getOtherHalf() {
return leftSide;
}
}
public class LeftSide implements Half{
private RightSide rightSide;
public void setRightSide(RightSide rightSide){
this.rightSide = rightSide;
}
#Override
public RightSide getOtherHalf(){
return rightSide;
}
}
Here is one solution. I have removed references to L for "left side" and R for "right side" and replaced them with T for "this side" and O for "other side"
public class Solution1 {
abstract static class Side<T extends Side<T, O>, O extends Side<O, T>> {
Side<O, T> _mate = null;
Side<O, T> getOtherSide() {
return _mate;
}
void setOtherSide(Side<O, T> mate) {
_mate = mate;
}
void connectToMate() {
getOtherSide().setOtherSide(this);
}
}
//You concrete implementations can replace This and Other
//with Right and Left as you see fit.
static class RightSideSub extends Side<RightSideSub, LeftSideSub> {
}
static class LeftSideSub extends Side<LeftSideSub, RightSideSub> {
}
}
However, I would suggest another, simpler solution at the cost of loosing connectToMate() method and replacing it with code in main() method below:
class OtherSolution{
abstract static class Side<O>{
O _mate = null;
void setOtherSide(O mate){
_mate = mate;
}
}
static class RightSideSub extends Side<LeftSideSub> {
}
static class LeftSideSub extends Side<RightSideSub> {
}
public static void main(String[] args) {
RightSideSub rs = new RightSideSub();
LeftSideSub ls = new LeftSideSub();
//do the connectToMate() operation externally like this:
rs.setOtherSide(ls);
ls.setOtherSide(rs);
}
}

Categories

Resources