Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make a generic List which can accept two different, unrelated types?

I have to define a List and it has two types of possible values

  1. String
  2. Some user defined Class

How can I make a List that is type safe in that it only accepts these two types?

I want to avoid the use of raw List.

like image 525
Ashish Agarwal Avatar asked Aug 29 '09 12:08

Ashish Agarwal


People also ask

Can generics take multiple type parameters?

A Generic class can have muliple type parameters.

How do you add two generic values in Java?

You have to add the numbers as the same type, so you could do x. intValue() + y. intValue(); .

Can you have a List of different types in Java?

Thus there are four types of lists in Java i.e. Stack, LinkedList, ArrayList, and Vector.


2 Answers

I'm not going to claim that this is a perfect solution, but I'm going to recommend that you go with a "holder" class - also called a "tagged class" by some writers (including Joshua Bloch, who says that tagged classes are "verbose, error-prone, and inefficient").

However, given your situation, I can't see a better way. The solution below provides:

  • Compile-time type safety on insertion of types with no possible common ancestor
  • The ability to replace your current List with some other collection without having to implement and test any additional code
  • The ability to use the holder class in other contexts

Youd define your holder class like this:

class Holder {
  private String foo;
  private UserClass bar;
  boolean isString;
  boolean initialized=false;
  Holder (String str) { foo = str; isString=true; }
  Holder (UserClass bar) { this.bar = bar; isString=false; }
  String getStringVal () { 
      if (! initialized) throw new IllegalStateException ("not initialized yet");
      if (! isString) throw new IllegalStateException ("contents not string");
      return foo;
  }
  // with a similar method for getUserClassVal()
...
}

Another alternative is to use an enum for the tag, rather than the boolean isString - this has the value of being easily extensible to additional types.

Then of course you'd have your compound list:

   List samsList = new ArrayList()

Inserts are easy and, as you requested, compile-time type safe:

   samsList.add (new Holder(stringVal));
   samsList.add (new Holder(userClassVal));

Retrieving values from the list is only slightly more complicated: you have to check the tag (holder.isString()) before deciding which getter to use. As an example, a foreach iteration over the list would look like this:

   for (Holder holder: samsList) {
      if (holder.isString())
         doYourStringProcessing (holder.getStringVal());
      else
         doYourUserClassProcessing (holder.getUserClassVal());
   }

Like I said, I'm not claiming this is perfect, but it meets your requirements will serve your needs and minimize the burden on the caller.

However, I would like to point out that this feels to me as though it's probably cause to consider refactoring/redesign somewhere. One of the guidelines I follow is that whenever I find myself justifying an exception to sound practice, it deserves a lot more thought than simply "how can I do this?".

Here's why: assuming that I'm right that the exception is justified in this case, there's really only two possibilities. One is that the sound practice is incomplete (so that "Prefer X over Y" should be rewritten as "Prefer X over Y except in case Z").

But much more likely is that the underlying clause is an imperfect design, and we should be thinking hard about doing some redesign/refactoring.

like image 108
CPerkins Avatar answered Oct 23 '22 03:10

CPerkins


Since String is an immediate subclass of Object and is final, you won't find a common supertype between String and your user-defined class other than Object. So List<Object> is what you have to use.

From a design perspective, mixing unrelated classes in a collection is a bad idea. Think about what you're trying to accomplish, and you'll probably come up with a better approach.

like image 23
kdgregory Avatar answered Oct 23 '22 04:10

kdgregory