chrisdotcode
chrisdotcode

Reputation: 1591

Why do we first declare subtypes as their supertype before we instantiate them?

Reading other people's code, I've seen a lot of:

List<E> ints = new ArrayList<E>();
Map<K, V> map = new HashMap<K, V>();

My question is: what is the point/advantage of instantiating them that way as opposed to:

ArrayList<E> ints = new ArrayList<E>();
HashMap<K, V> map = new HashMap<K, V>();

What also makes it odd is that I've never seen anything like:

CharSequence s = new String("String");

or

OutputStream out = new PrintStream(OutputStream);


Duplicates (of the first part of the question):

When/why to use/define an interface

Use interface or type for variable definition in java?

When should I use an interface in java?

why are interfaces created instead of their implementations for every class

What's the difference between these two java variable declarations?

Upvotes: 21

Views: 3007

Answers (12)

Rostislav Matl
Rostislav Matl

Reputation: 4543

Using interfaces has the main advantage that you can later change the implementation (the class) without the need to change more than the single line where you create the instance and do the assignment.

Upvotes: 11

viktor
viktor

Reputation: 1297

@Bhushan answered why. To answer your confusion Why nobody uses

CharSequence s = new String("String");

or

OutputStream out = new PrintStream(OutputStream);

CharSequence contains only few common methods. Other classes that implement this interface are mostly buffers and only String is immutable. CharSequence defines common api for classes backed by char array and This interface does not refine the general contracts of the equals and hashCode methods (see javadoc).

OutputStream is low-level api for writing data. Because PrintStream adds extra convenient methods for writing - higher level of abstraction, it's used over OutputStream.

Upvotes: 4

A.H.
A.H.

Reputation: 66263

The reason behind this is not technical but the stuff you have to read between the lines of code: The List and Map examples says: "I'm only interested in basic list/map stuff, basically you can use anything here." An extreme example of that would be

Iterable<Foo> items = new ArrayList<Foo>();

when you really only want to do some stuff for each thing.

As an added bonus this makes it a little easier to refactor the code later into common utility classes/methods where the concrete type is not required. Or do you want to code your algorithm multiple times for each kind of collection?

The String example on the other hand is not seen wildly, because a) String is special class in Java - each "foo" literal is automatically a String and sooner or later you have to give the characters to some method which only accepts String and b) the CharSequence is really ahh minimal. It does not even support Unicode beyond the BMP properly and it misses most query/manipulation methods of String.

Upvotes: 3

Sachin
Sachin

Reputation: 18747

For

List<E> ints = new ArrayList<E>();
Map<K, V> map = new HashMap<K, V>();

List and Map are the interfaces, so any class implementing those interfaces can be assigned to these references.

ArrayList is one of the several classes (another is LinkedList) which implement List interface.

Same with Map. HashMap, LinkedHashMap, TreeMap all implement Map.

It is a general principle To program for interfaces and not for implementations. Due to this, the programming task becomes easier. You can dynamically change the behavior of the references.

If you write

ArrayList<E> ints = new ArrayList<E>();
HashMap<K, V> map = new HashMap<K, V>();

ints and map will be ArrayList and HashMap only, forever.

Upvotes: 7

OscarRyz
OscarRyz

Reputation: 199215

Is a design principle that you program to the interface and not to the implementation.

That way you may provide later a new implementation to the same interface.

From the above link Eric Gamma explains:

This principle is really about dependency relationships which have to be carefully managed in a large app. It's easy to add a dependency on a class. It's almost too easy; just add an import statement and modern Java development tools like Eclipse even write this statement for you. Interestingly the inverse isn't that easy and getting rid of an unwanted dependency can be real refactoring work or even worse, block you from reusing the code in another context. For this reason you have to develop with open eyes when it comes to introducing dependencies. This principle tells us that depending on an interface is often beneficial.

Here, the termin interface refers not only to the Java artifact, but the public interface a given object has, which is basically composed of the methods it has, so, it could be a Java interface ( like List in your example ) or a concrete superclass.

So in your example if you ever want to use a LinkedList instead it would be harder because the type is already declared as ArrayList when just list would've been enough.

Of course, if you need specific methods from a given implementation, you have to declare it of that type.

I hope this helps.

Upvotes: 5

Paul Medcraft
Paul Medcraft

Reputation: 1406

This is programming to the interface not the implementation, as per the Gang of Four. This will help to stop the code becoming dependent on methods that are added to particular implementations only, and make it easier to change to use a different implementation if that becomes necessary for whatever reason, e.g. performance.

Upvotes: 1

Bhesh Gurung
Bhesh Gurung

Reputation: 51030

 List<E> ints = new ArrayList<E>();

If you write some code that deals only with List then it will work for any class that implements List (e.g. LinkedList, etc). But, if your code directly deals with ArrayList then it's limited to ArrayList.

CharSequence s = new String("String");

Manually instantiating a String object is not good. You should use string literal instead. I am just guessing the reason that you don't see CharSequence might because it's quite new and also, strings are immutable.

Upvotes: 1

Perception
Perception

Reputation: 80603

Quick answer? Using interfaces and superclasses increases the portability and maintainability of your code, principally by hiding implementation detail. Take the following hypothetical example:

class Account {
    private Collection<Transaction> transactions;

    public Account() {
        super();
        transactions = new ArrayList<Transaction>(4);
    }

    public Collection<Transaction> getTransactions() {
        return transactions;
    }
}

I've declared a contract for an Account that states that the transactions posted to the account can be retrieved as a Collection. The callers of my code don't have to care what kind of collection my method actually returns, and shouldn't. And that frees me to change up the internal implementation if I need to, without impacting (aka breaking) unknown number of clients. So to wit, if I discover that I need to impose some kind of uniqueness on my transactions, I can change the implementation shown above from an ArrayList to a HashSet, with no negative impact on anyone using my class.

public Account() {
    super();
    transactions = new HashSet<Transaction>(4);
}

As far as your second question, I can say that you use the principal of portability and encapsulation wherever they make sense. There are not a terrible lot of CharSequence implementations out there, and String is by far the most used common. So you just won't see alot of developers declaring CharSequence variables in their code.

Upvotes: 24

Has QUIT--Anony-Mousse
Has QUIT--Anony-Mousse

Reputation: 77454

When you at some point decide to use a different implementation, say:

List<E> ints = new LinkedList<E>();

instead of

List<E> ints = new ArrayList<E>();

this change needs to be done only at a single place.

There is the right balance to strike:

usually you use the type which gives you the most appropriate guarantees. Obviously, a List is also a Collection which is also something Iterable. But a collection does not give you an order, and an iterable does not have an "add" method.

Using ArrayList for the variable type is also reasonable, when you want to be a bit more explicit about the need for fast random access by object position - in a LinkedList, a "get(100)" is a lot slower. (It would be nice if Java had an interface for this, but I don't think there is one. By using ArrayList, you disallow casting an array as list.)

Upvotes: 1

alf
alf

Reputation: 8513

It is just easier to think of String as of String. As well as it's easier (and more beneficial) to think of WhateverList as of List.

The bonuses are discussed many times, but in brief you simply separate the concerns: when you need a CharSequence, you use it. It's highly unlikely that you need ArrayList only: usually, any List will do.

Upvotes: 1

Jaroslav Z&#225;ruba
Jaroslav Z&#225;ruba

Reputation: 4876

You do this to make sure later when working with the variable you (or anyone using your classes) won't rely on methods specific for the implementation chosen (ArrayList, HashMap, etc.)

Upvotes: 3

Cameron S
Cameron S

Reputation: 2301

This (good) style of declaring the type as the Interface the class implements is important because it forces us to use methods only defined in the Interface.

As a result, when we need to change our class implementations (i.e. we find our ArraySet is better than the standard HashSet) we are guaranteed that if we change the class our code will work because both classes implement the strictly-enforced Interface.

Upvotes: 2

Related Questions