简体   繁体   中英

Should I extend an ArrayList (is-a) or should I include it as a member (has-a)?

I'm making a simple program that maintains a list of numbers, and I want this list to also have a name. Which is the best approach: have my list class extend ArrayList or have it include an ArrayList member? In both cases, there would of course be a "name" String member.

The first approach means I only have to implement a getter & setter for the name, but I think this would tie my class too closely to a particular implementation? For example, if I wanted to later use a Vector, than I would have to change code everywhere.

The second approach would make it easier to change the implementation, but of course becomes quite annoying for now, since I have to implement a bunch of wrappers.

I've read the SO posts regarding inheritance vs. composition, and since my list is a type of ArrayList, I am leaning towards the first approach. However, is there any differences to the discussion because I'm extending a Collection class vs extending a general class? Or am I over-thinking this?

In the long run it's generally better to include as a member than extend. This way it's more explicit what you want to allow, making it easier to test and hold to the contact of the class. If you simply extend ArrayList then it might not always be used as you intended. The trade-off of course is that you'll have to explicitly create pass-through methods for everything you do want to allow. Alternatively (or additionally) you might want to provide a method to get the underlying List, which you could wrap with an immutable collection to safeguard it from changes happening outside of the control of your class.

For the best of both worlds, use a Guava ForwardingList . Here's a simple example:

public class NamedList<E> extends ForwardingList<E> implements RandomAccess {
  // could also let the user provide the delegate list
  private final List<E> delegate = Lists.newArrayList();
  private String name;

  @Override protected List<E> delegate() {
    return delegate;
  }

  // constructors, getter, setter
}

By the way, the pitfalls of extending a concrete collection implementation rather than using composition are discuseed in Effective Java item 16 (in 2nd Ed.) "Favor composition over inheritance." One of the several issues mentioned has to do with unexpected behavior related to the interaction between methods in the superclass (eg add and addAll ).

Guava's Forwarding* classes are an implementation of the solution suggested there.

Neither is better, its a trade-off as you mentioned.

If you do aggregation (has-a) you have to wrap all the functionality of the member you're going to use (ie implement functions that call them)

If you do inheritance, A lot of functions are added to your new class which might be unneccessary, And you need to implement abstract functions as well.

If you were in a C++ environment, private inheritance would be an option, But here both have pros and cons

Composition - HAS-A. I prefer it for collections of all stripes.

@AbiusX is right there is trade off. But I have asollution for your worry with first approach. Please consider following and let me know what problems are with this approach:

public class MyClass implements List
{
    private String name;
    private List myList = new ArrayList();

    public MyClass(String name)
    {
        this.name = name;
    }

    public String getName()
    {
        return name;
    }

    public void setName()
    {
        return name;
    }

    @Override
    public void add(int index, Object element)
    {
        myList.add(index,element);
    }

    @Override
    public boolean add(Object o)
    {
        myList.add(o);
    }

    @Override
    public boolean addAll(Collection c)
    {
        myList.addAll(c);
    }

    //so on for rest of methods in List interface
}

We can also include Generics if you want to make it type safe for numbers.

Inheriting ArrayList is not a good idea, you need a strong reason to do that. Utility classes like Collections provide the necessary functionality in several flavors, so you really need to add some extra required functionality in order to justify the subclassing, as is a very well-know class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM