Java using generics with lists and interfaces - java

Ok, so here is my problem:
I have a list containing interfaces - List<Interface> a - and a list of interfaces that extend that interface: List<SubInterface> b. I want to set a = b. I do not wish to use addAll() or anything that will cost more memory as what I am doing is already very cost-intensive. I literally need to be able to say a = b. I have tried List<? extends Interface> a, but then I cannot add Interfaces to the list a, only the SubInterfaces. Any suggestions?
I want to be able to do something like this:
List<SubRecord> records = new ArrayList<SubRecord>();
//add things to records
recordKeeper.myList = records;
The class RecordKeeper is the one that contains the list of Interfaces (NOT subInterfaces)
public class RecordKeeper{
public List<Record> myList;
}

This works :
public class TestList {
interface Record {}
interface SubRecord extends Record {}
public static void main(String[] args) {
List<? extends Record> l = new ArrayList<Record>();
List<SubRecord> l2 = new ArrayList<SubRecord>();
Record i = new Record(){};
SubRecord j = new SubRecord(){};
l = l2;
Record a = l.get( 0 );
((List<Record>)l).add( i ); //<--will fail at run time,see below
((List<SubRecord>)l).add( j ); //<--will be ok at run time
}
}
I mean it compiles, but you will have to cast your List<? extends Record> before adding anything inside. Java will allow casting if the type you want to cast to is a subclass of Record, but it can't guess which type it will be, you have to specify it.
A List<Record> can only contain Records (including subRecords), A List<SubRecord> can only contain SubRecords.
But A List<SubRecord> is not a List<Record> has it cannot contains Records, and subclasses should always do what super classes can do. This is important as inheritance is specilisation, if List<SubRecords> would be a subclass of List<Record>, it should be able to contain ` but it'S not.
A List<Record> and a List<SubRecord> both are List<? extends Record>. But in a List<? extends Record> you can't add anything as java can't know which exact type the List is a container of. Imagine you could, then you could have the following statements :
List<? extends Record> l = l2;
l.add( new Record() );
As we just saw, this is only possible for List<Record> not for any List<Something that extends Record> such as List<SubRecord>.
Regards,
Stéphane

Just to explain why Java does not permit this:
A List<Record> is a list in which you can put any object implementing Record, and every object you get out will implement Record.
A List<SubRecord> is a list in which you can put any object implementing SubRecord, and every object you get out will implement SubRecord.
If it would be allowed to simply use a List<SubRecord> as a List<Record>, then the following would be allowed:
List<SubRecord> subrecords = new ArrayList<SubRecord>();
List<Record> records = subrecords;
records.add(new Record()); // no problem here
SubRecord sr = subrecords.get(0); // bang!
You see, this would not be typesafe. A List (or any opject of a parametrized class, in fact) can not typesafely change its parameter type.
In your case, I see these solutions:
Use List<Record> from start. (You can add SubRecord objects to this without problems.)
as a variation of this, you can use List<? super Subrecord> for the method which adds stuff. List<Record> is a subtype of this.
copy the list:
List<Record> records = new ArrayList<Record>(subrecords);
To exand a bit on th variation:
void addSubrecords(List<? super Subrecord> subrecords) {
...
}
List<Record> records = new ArrayList<Record>();
addSubrecords(records);
recordkeeper.records = records;

THIS WORK, BUT YOU SHOULD BE WARN !!!!
#Override
// Patient is Interface
public List<Patient> getAllPatients() {
// patientService.loadPatients() returns a list of subclasess of Interface Patient
return (List<Patient>)(List<? extends Patient>)patientService.loadPatients();
}
You can cast from List of Objects to List of Interface in this way.
But, if you get this list somewhere in your code and if you try to add something to this list, what would you add? Inteface or Subclass of this interface ? You actually loose information of the type of list, because you let it hold Interface, so you can add anything that implement this interface, but the list is holding the subclasses only, and you could easily get class cast exception if you try to do operations like add or get on this list with some other subclass. The solution is: Change The type of source list to list<Interface> instead of cast, then you are free to go :)

You can't do that and be safe because List<Interface> and List<SubInterface> are different types in Java. Even though you can add types of SubInterface to a list of Interface, you can't equate the two lists with different interfaces even if they're sub/super interfaces of eachother.
Why is it that you want to do b = a so bad? Do you just want to store a reference to the SubInterface list?
On a side note, I suggest you read this documentation on the oracle site: http://download.oracle.com/javase/tutorial/java/generics/index.html
It explains and goes deep into generics very well.

So, the rather simple solution a friend of mine found was this:
recordKeeper.myList = (List<Record>)(List<? extends Record>)records;
This works as I understand it because it takes baby steps. List<SubRecord> is a List<? extends Record>, and List<? extends Record> is a List<Record>. It might not be pretty, but it works nonetheless.

There's no way to do it that is type safe.
A List<SubInterface> cannot have arbitrary Interfaces added to it. It is a list of SubInterfaces after all.
If you are convinced that this is safe to do even though it is not type safe you can do
#SuppressWarnings("unchecked")
void yourMethodName() {
...
List<Interface> a = (List<Interface>) b;
...
}

Related

Typing a generic type but not its own type in Java Generics

I need to type method signature so it accepts 2 equally typed parameters of different particular concrete subtypes.
Is it possible to code something like this with generics? How would you solve it? (The case is absolutely an example)
public <T extends List<?>> T<String> sum(T<Integer> sublistOfInts, T<Boolean> sublistOfBooleans){
/*fusion both lists*/
return sublistOfStrings;
}
EDIT: In the end, what I am looking for is a way for the compiler to pass:
ArrayList<String> myList = sum(new ArrayList<Integer>(), new ArrayList<Boolean>());
but not:
ArrayList<String> myList = sum(new ArrayList<Double>(), new ArrayList<Boolean>());
nor
ArrayList<String> myList = sum(new LinkedList<Integer>(), new ArrayList<Boolean>());
(...)
EDIT 2: I found a better example. Imagine an interface Tuple, with child classes Duple, Triple>..., it would be perfectly nice to have something like
<T extends Tuple<?>> T<String> reset( T<String> input, T<Boolean> listToNull){
T copy = input.copy();
for (int i=0; i<input.size();i++){
if (listToNull.get(i)){
copy.set(i,null);
}
}
}
What I suggest you do instead
First, get rid of the method argument generics. There's no reason to force a caller to provide ArrayList<Integer> and ArrayList<Boolean> when you want to return an ArrayList<String>. Just accept any List<Integer> and List<Boolean>, and leave it to your method to turn them into the appropriate return List.
Since you know that you want to return some sort of List of String you can write your parameter as <T extends List<String>> and your return type as simply T.
That leaves us with the hard part: getting your method to instantiate an object of unknown type. That's hard. You can't just do new T();. You need to invoke something that will produce a T on your behalf. Luckily, Java 8 provides a Functional Interface for Supplier<T>. You just need to invoke the get() method to get your ArrayList<String> or whatever else you might want. The part that's painful is that your invoker needs to provide their own Supplier. But I think that's as good as it gets in Java 8.
Here's the code:
public <T extends List<String>> T sum(
List<Integer> sublistOfInts,
List<Boolean> sublistOfBooleans,
Supplier<T> listMaker) {
T sublistOfStrings = listMaker.get();
/*fusion of both lists*/
return sublistOfStrings;
}
At least this compiles:
ArrayList<String> myNewList = thing.<ArrayList<String>>sum(intList, boolList, ArrayList::new);
And this does not:
ArrayList<String> myNewList = thing.<ArrayList<String>>sum(intList, boolList, LinkedListList::new);
You can even leave off the type parameter on the invocation. This compiles:
ArrayList<String> myNewList = thing.sum(intList, boolList, ArrayList::new);
And this does not:
ArrayList<String> myNewList = thing.sum(intList, boolList, LinkedListList::new);
Why you can't just do what you're asking
In brief, it's because type arguments can't themselves be parameterized. And that's because we don't know how many type arguments they themselves would take, nor the restrictions that might be placed on them.
Take the relatively obscure class RoleList. It extends ArrayList<Object>, so it fits List<?>. But it doesn't take a type argument at all. So if someone invoked your sum() method with RoleList, that would require in your example:
RoleList<Integer> intList = // something
RoleList<Boolean> boolList = // something
RoleList<String> myNewList = thing.sum(intList, boolList);
That clearly can't work since it requires an unparameterized type to take type arguments. And if you took off the type arguments like so:
RoleList intList = // something
RoleList boolList = // something
RoleList myNewList = thing.sum(intList, boolList);
Then your method needs to be able to accept two List<Object> arguments and return a value of List<Object>. And that violates your basic premise, that you be able to control such things.
In reality, RoleList should not be allowed here at all, because you can't ever guarantee that one instance will contain only Integers, another only Booleans, and a third only Strings. A compiler that allowed RoleList here would necessarily have weaker type checking than we have now.
So the bottom line is that you just can't do what you're asking because Java just isn't built that way.
Why that's ok
You can still get complete type safety inside your sum() method using my suggested method, above. You make sure that the incoming Lists contain only Integer or Boolean values, respectively. You make sure that the caller can rely on the return of a specific subtype of List containing only String values. All of the guarantees that make a difference are there.
There are two things that strike me about the above. How are you instantiating sublistOfStrings, and what advantages do you expect to get above using plain old inheritance?
There are a couple of ways of instantiating T<String>. You could have a factory check the class of your arguments, and instantiate it based on that. Or you could do something like:
(List<String>)sublistOfInts.getClass().newInstance()
But you can't just go new T<String>(). So you're basing the implementation of your return type off of the type of one of your arguments anyway (unless there's a way I haven't thought of).
By specifying both arguments are of type 'T' doesn't mean they're exactly of the same concrete type 'T' either. For instance
sum((int)1, (long)2L); // valid
sum((int)2, (double)2.0D); // valid ... etc
public <T extends Number> T sum(T a, T b) {
return a;
}
So you aren't enforcing that sublistOfInts and sublistOfBooleans are both of type say ArrayList, and therefore you can return an ArrayList. You still need to write code to check what type of List<?> you'll want to return based on the arguments.
I think you're better off not using generics, and using something like this:
public List<String> sum(List<Integer> sublistOfInts, List<Boolean> sublistOfBooleans) {
// Determine what subclass of list you want to instantiate based on `sublistOfInts` and `sublistOfBools`
// Call factory method or newInstance to instantiate it.
// Sum, and return.
}
You can still call it with subtypes of List<?>. I don't beleive there's any advantage you could get from generics even if Java did let you do it (which is doesn't, because it can't parameterize T like that).
I know what you have is just an example but if you only want to return a single list that contains the String value of all the contents in a group of other lists you could just specify a method that takes a varargs of unbounded lists.
public List<String> sum(List<?>... lists) {
List<String> sublistOfStrings = new ArrayList<String>();
for(List<?> list : lists) {
for(Object obj : list) {
sublistOfStrings.add(obj.toString());
}
}
return sublistOfStrings;
}

Method to convert an Arraylist into a Set

I coded the following method to convert my Arraylist into a set:
public static Set<Animal> toSet(){
Set<Animal> aniSet = new HashSet<Animal>(animals);
return aniSet;
}
I would like to do this instead :
public static Set<Animal> toSet(){
return HashSet<Animal>(animals);
}
Why do i get an error message that says it cannot find variable HashSet ? Do i need to store a variable first ?
EDIT : had to add new before my Hashset. Coding makes me feel so dumb :')
There are two problems with this code:
You forget that the animals have to come from somewhere; I don't think the first example compiles either; and
you forgot to use new when creating a new HashSet<Animal>.
This is probably the intended behavior:
public static <T> Set<T> toSet(Collection<? extends T> data){
return new HashSet<T>(data);
}
You can then call it with:
ArrayList<Animal> animals = new ArrayList<>();
//do something with the animals list
//...
Set<Animal> theSet = Foo.<Animal>toSet(animals);
by using a generic static method, you can call it with any type you like. By using Collection<? extends T> you are furthermore not limited to an ArrayList<T>, but you can use any kind of Collection (LinkedList, HashSet, TreeSet, ...). Finally the type of that collection does not even have to be animal. You could convert an ArrayList<Cat> into a HashSet<Animal>.
Note however that there is not much use in this method: calling it is not much shorter than using the constructor directly. The only real advantage I see is that you encapsulate which Set<T> you are going to use, such that if you later change your mind to TreeSet<T> all methods calling this toSet method will generate a TreeSet<T> instead of a HashSet<T>.

Automatically merge several collections to one

I have some Guava Functions like Function<String,Set<String>>. Using those with FluentIterable.transform() leads to a FluentIterable<Set<String>>, however I need a FluentIterable<String>. So my idea now would be to subclass FluentIterable<E> and add a new method transform2() which simply merges everything to one collection before returning it.
The original transform method looks like this:
public final <T> FluentIterable<T> transform(Function<? super E, T> function) {
return from(Iterables.transform(iterable, function));
}
I thought of something like this for my subclass and transform2() method:
public abstract class FluentIterable2<E> extends FluentIterable<E>
{
public final <T> FluentIterable<T> transform2(Function<? super E, Collection<T>> function) {
// (PROBLEM 1) Eclipse complains: The field FluentIterable<E>.iterable is not visible
Iterable<Collection<T>> iterables = Iterables.transform(iterable, function);
// (PROBLEM 2) Collection<T> merged = new Collection<T>(); // I need a container / collection - which one?
for(Collection<T> iterable : iterables)
{
// merged.addAll(iterable);
}
// return from(merged);
}
}
Currently I have two problems with my new subclass, marked above with PROBLEM 1 and PROBLEM 2
PROBLEM 1: The iterable field in the original FluentIterable class is private - what can I do about this? Can I create a new private field with the same name in my subclass, will this then be OK? What about methods in my subclass that call super.someMethod() which uses this field? Will they then use the field of the super class, which probably has a different value?
PROBLEM 2: I need some generic collection where I can combine the content of several collections, but collections is an interface, so I can't instantiate it. So, which class can I use there?
It would be acceptable if the solution only works with sets, though I'd prefer a solution that works with sets and lists.
Thanks for any hint on this!
Does FluentIterable.transformAndConcat(stringToSetFunction) not work for your use case?
Why subclass FluentIterable just to do this? You just need a simple loop:
Set<String> union = Sets.newHashSet();
for (Set<String> set : fluentIterableOfSets) {
union.addAll(set);
}
Use FluentIterable.transformAndConcat(f), where f is a Function mapping an element to some kind of iterable over the element type.
In your case, let's say your Function<String, Set<String>> is called TOKENIZE, and your initial Iterable<String> is called LINES.
Then to get a Set<String> holding all the distinct tokens in LINES, do this:
Iterable<String> LINES = ...;
Function<String, Set<String>> TOKENIZE = ...;
Set<String> TOKENS = FluentIterable.from(LINES)
.transformAndConcat(TOKENIZE)
.toSet();
But consider JB Nizet's answer carefully. Try it both ways and see which works better.

Add a list in another list with different types

In my Java EE project, I have 2 ArrayList objects:
private ArrayList<Sales_personRef> sales_personsRef = new ArrayList<Sales_personRef>();
private ArrayList<Sales_person> sales_persons = new ArrayList<Sales_person>();
My question is : How can I add my first list(sales_personsRef) in the second list?
These lists have different types and I cannot cast it...
If Sales_personRef and Sales_person both extend another class, let's say Person, then you could add both of those types to an ArrayList<Person>.
Keep in mind that if you do it this way, then what you get out of the list later will be of type Person. You will then have to cast it back to its original class.
You need to convert each items of the first list in the items accepted by the second List
sales_persons.addAll(convertSalesPersons(sales_personsRef));
with:
private static Collection<? extends Sales_person> convertSalesPersons(ArrayList<Sales_personRef> sales_persons) {
List<Sales_person> persons = new ArrayList<Sales_person>();
for (Sales_personRef sales_personRef : sales_persons) {
persons.add(converSalesPersonRefToSalesPerson(sales_personRef));
}
return persons;
}
private static Sales_person converSalesPersonRefToSalesPerson(Sales_personRef sales_personRef) {
//implement the conversion here
return new Sales_person();
}
By declaring your second list as an List<Object>.
you have to use polymorphism concept here . make a parent class say person and then extend it that would work

Should I declare/Initialize ArrayLists as Lists, ArrayLists, or ArrayLists of <Cat>

What is the difference in declaring a collection as such
public class CatHerder{
private List cats;
public CatHerder(){
this.cats = new ArrayList<Cat>();
}
}
//or
public class CatHerder{
private ArrayList cats;
public CatHerder(){
this.cats = new ArrayList();
}
}
//or
public class CatHerder{
private ArrayList<Cat> cats;
public CatHerder(){
this.cats = new ArrayList<Cat>();
}
}
You should declare it as a List<Cat>, and initialize it as an ArrayList<Cat>.
List is an interface, and ArrayList is an implementing class. It's almost always preferable to code against the interface and not the implementation. This way, if you need to change the implementation later, it won't break consumers who code against the interface.
Depending on how you actually use the list, you might even be able to use the less-specific java.util.Collection (an interface which List extends).
As for List<Cat> (you can read that as "list of cat") vs List: that's Java's generics, which ensure compile-time type safely. In short, it lets the compiler make sure that the List only contains Cat objects.
public class CatHerder{
private final List<Cat> cats;
public CatHerder(){
this.cats = new ArrayList<Cat>();
}
}
I would do the following.
public class CatHerder{
private final List<Cat> cats = new ArrayList<Cat>();
}
In order to ensure type safety, and because current Java compilers will complain if a generic type has no type argument, you should always specify a type explicitly - or <?> if you really don't care.
That said, unless you use something specific to the ArrayList class, you should use List<Cat> to avoid tying your code to a particular List implementation.
As Matt already stated, using the most common Interface/Superclass is the best way to go here.
Make sure to always declare the Type that appears in your List, so make it a List<Cat> or even List<? extends Cat>
If, at some later point, you want to replace the ArrayList with, say, a LinkedList, you won't have to change the declaration, but only the instantiation.
List is more flexible than ArrayList, List<Cat> is safer than List. so List<Cat> is good choice.
First of all, List is an interface and ArrayList is an implementation of the List interface (actually, it subclasses AbstractList and implements List). Therefore List cats = new ArrayList() is valid since ArrayList is-a List.
For this:
private List cats;
cats becomes a raw-type (there is no reference to the Generic Type for List), it hasn't been parameterised.
Your 3rd solution is correct (it solves your problem for option 1),
private ArrayList<Cat> cats;
you have bounded a Generic Type E for List<E> to a type Cat. Therefore, your instantiation of cats is valid as the generic bounding is the same.
Your 2nd solution allows that only ArrayList of cats can be instantiated. The other 2 options allows you to instantiate any object that is-a List, e.g. LinkedList.

Categories