Parameterized collection catch-22

  • Thread starter zapanaz_googlegroups
  • Start date
Z

zapanaz_googlegroups

I have a parent class, AddressInfo. An abstract class. Two classes inherit from AddressInfo, ShippingInfo and BillingInfo. ShippingInfo and BillingInfo are nearly identical, and are identical for what I'm doing here.

The example code I'm posting is stripped down, in case you wonder why it's a little dumb.

I have two methods,

public void mergeBillingInfoLists(List<BillingInfo> list1, List<BillingInfo> list2) {
list1.add(1, list2.get(0));
}

And one for ShippingInfo which is identical, just with ShippingInfo where BillingInfo is.

I want to combine the two methods, which are very redundant. It seems like it should be possible, given that they have a common parent class. I can dothis:

public void mergeAddressInfo(List<? extends AddressInfo> list1, List<? extends AddressInfo> list2) {
list1.add(1, list2.get(0));
}

But Java won't allow this, it won't allow the add(). All it knows about what list2 contains is that it's something that descends from AddressInfo, so even if I pass in 2 List<BillingInfo>'s, inside the code it doesn't know they're the same classes.

Does anybody offhand know a way to do this? (Without throwing away the parameterization :) )

I don't necessarily need to use this exact approach, just anything that would allow me to combine the two methods into one method. In fact I have quite a lot of this kind of redundancy in the code base I'm working with, so this is something I'm running into a lot.

Thanks for any help

--
Joe Cosby
http://joecosby.com/
`She knew how to embroider and milk a cow.' -- Connie Willis, Doomsday Book

:: Currently listening to Oxygene, Pt. II, 1976, by Jean Michel Jarre, from"Oxygene"
 
M

markspace

public void mergeAddressInfo(List<? extends AddressInfo> list1,
List<? extends AddressInfo> list2)

{
list1.add(1, list2.get(0));
}

But Java won't allow this, it won't allow the add(). All it knows
about what list2 contains is that it's something that descends from
AddressInfo, so even if I pass in 2 List<BillingInfo>'s, inside the
code it doesn't know they're the same classes.

Yes. Consider that your two subtypes, BillingInfo and ShippingInfo,
both match the wildcard <? extends AddressInfo>. So it's perfectly
legal to pass one of each to the mergeAddressInfo() method.

List<BillingInfo> b = ...;
List<ShippingInfo> s = ...;

x.mergeAddressInfo( b, s ); // works

So Java is telling you you can't call list1.add() because it assumes you
don't want to mix up a list of two different types.

So you have to tell the compiler that both parameters are of the same
type. One way to do that is to use a generic method:

public <A extends AddressInfo> void mergeAddressInfo( List<A> a,
List<A> b )
{...

Now the compiler knows that a and b are of the same type. It will
enforce that when you call the method (give you an error) and it will
allow operations that make sense for lists of the same type.

(Not compiled or tested.)

C.f. the Java tutorial.

<http://docs.oracle.com/javase/tutorial/java/generics/capture.html>
<http://docs.oracle.com/javase/tutorial/java/generics/methods.html>
 
D

Daniel Pitts

I have a parent class, AddressInfo. An abstract class. Two classes inherit from AddressInfo, ShippingInfo and BillingInfo. ShippingInfo and BillingInfo are nearly identical, and are identical for what I'm doing here.

The example code I'm posting is stripped down, in case you wonder why it's a little dumb.

I have two methods,

public void mergeBillingInfoLists(List<BillingInfo> list1, List<BillingInfo> list2) {
list1.add(1, list2.get(0));
}

And one for ShippingInfo which is identical, just with ShippingInfo where BillingInfo is.

I want to combine the two methods, which are very redundant. It seems like it should be possible, given that they have a common parent class. I can do this:

public void mergeAddressInfo(List<? extends AddressInfo> list1, List<? extends AddressInfo> list2) {
list1.add(1, list2.get(0));
}

But Java won't allow this, it won't allow the add(). All it knows about what list2 contains is that it's something that descends from AddressInfo, so even if I pass in 2 List<BillingInfo>'s, inside the code it doesn't know they're the same classes.

Does anybody offhand know a way to do this? (Without throwing away the parameterization :) )

I don't necessarily need to use this exact approach, just anything that would allow me to combine the two methods into one method. In fact I have quite a lot of this kind of redundancy in the code base I'm working with, so this is something I'm running into a lot.

Thanks for any help

There are three ways write that so it works as you desire:

Method one:
public <T> void mergeInfo(List<? super T> list1,List<? extends T> list2)

T is any type at all.
list1 can be any type of List of <T> or List of <SuperclassOfT> This
means the list1 can accept any object that is assignable *from* <T>. It
is unknown the type that it produces.

list2 can be any type of List of <T> or List of <SubclassOfT>. It will
produce objects that assignable *to* <T>.



Method two:
public <T> void mergeInfo(List<T> list1,List<? extends T> list2)

T is any type at all.
list1 can be any type of List of <T>, but not any other type parameter.
list1 can therefor produce and accept any object assignable to or from <T>

list2 is the same as list2 from Method One


Method three:
public <T> void mergeInfo(List<T> list1,List<T> list2)

T is any type at all.
list1 can be any type of List of <T>, but not any other type parameter.
list1 can therefor produce and accept any object assignable to or from <T>

list2 must be a List with the exact same type parameter as list1.


And finally, one last note. For any of the examples above, if you want
to ensure that you're only dealing with subclasses of AddressInfo:

public <T extends AddressInfo> void mergeInfo(... yata yata)


This says "T must be AddressInfo or a subclass thereof".

In your particular case, I would combine this with Method Three. Though
often with generic functions, you're better off with Method one.

The rule-of-thumb I use is: In your scope if you have an object that
only accepts <T> use <? super T>. If you have an object that only
produces <T>, use <? extends T>. If you have an object that produces
and accepts <T>, then use <T>. I'm talking about "produces" or
"accepts" in what you call, not what methods are available.

HTH.
 
Z

zapanaz_googlegroups

public <A extends AddressInfo> void mergeAddressInfo( List<A> a,

List<A> b )

{...



Now the compiler knows that a and b are of the same type. It will

enforce that when you call the method (give you an error) and it will

allow operations that make sense for lists of the same type.

Ah thanks very much. I should have known that.

They added generics to Java after I learned it, I still don't think of them.
(Not compiled or tested.)

it's OK, I see what you're saying


--
Joe Cosby

Agent 99: Max, that knife missed you by inches.
Maxwell Smart: You think it's some kind of warning?

:: Currently listening to All My Life, 1972, by Uriah Heep, from "Demons and Wizards"
 
Z

zapanaz_googlegroups

There are three ways write that so it works as you desire:



Method one:

public <T> void mergeInfo(List<? super T> list1,List<? extends T> list2)



T is any type at all.

list1 can be any type of List of <T> or List of <SuperclassOfT> This

means the list1 can accept any object that is assignable *from* <T>. It

is unknown the type that it produces.



list2 can be any type of List of <T> or List of <SubclassOfT>. It will

produce objects that assignable *to* <T>.







Method two:

public <T> void mergeInfo(List<T> list1,List<? extends T> list2)



T is any type at all.

list1 can be any type of List of <T>, but not any other type parameter.

list1 can therefor produce and accept any object assignable to or from <T>



list2 is the same as list2 from Method One





Method three:

public <T> void mergeInfo(List<T> list1,List<T> list2)



T is any type at all.

list1 can be any type of List of <T>, but not any other type parameter.

list1 can therefor produce and accept any object assignable to or from <T>



list2 must be a List with the exact same type parameter as list1.





And finally, one last note. For any of the examples above, if you want

to ensure that you're only dealing with subclasses of AddressInfo:



public <T extends AddressInfo> void mergeInfo(... yata yata)





This says "T must be AddressInfo or a subclass thereof".



In your particular case, I would combine this with Method Three. Though

often with generic functions, you're better off with Method one.



The rule-of-thumb I use is: In your scope if you have an object that

only accepts <T> use <? super T>. If you have an object that only

produces <T>, use <? extends T>. If you have an object that produces

and accepts <T>, then use <T>. I'm talking about "produces" or

"accepts" in what you call, not what methods are available.



HTH.


Thanks very much

--
Joe Cosby

Agent 99: Max, that knife missed you by inches.
Maxwell Smart: You think it's some kind of warning?

:: Currently listening to All My Life, 1972, by Uriah Heep, from "Demons and Wizards"
 
L

Lew

Ah thanks very much. I should have known that.

They added generics to Java after I learned it, I still don't think of them.

That was ten years ago. Two versions of Java have already gone obsolete since generics came in, and the fourth version to use them just came out.

You might want to consider that it's been long enough.
 
Z

zapanaz_googlegroups

That was ten years ago. Two versions of Java have already gone obsolete since generics came in, and the fourth version to use them just came out.



You might want to consider that it's been long enough.

OK Dad
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top