Does using generics complicate the use of interface-type declarations?

J

jan V

In a recent post I saw this line of code:
ArrayList<String> _hidden = new ArrayList<String>();

If we ignore the use of generics, we can rephrase this as
ArrayList _hidden = new ArrayList();

This code violates the principle that you should declare your variables to
be interface types, if at all possible (as per Joshua Bloch's
recommendations).

Does using generics make following this principle harder, or did the
original author of the above code simply not follow the rule?
 
S

Stefan Schulz

In a recent post I saw this line of code:


If we ignore the use of generics, we can rephrase this as


This code violates the principle that you should declare your variables to
be interface types, if at all possible (as per Joshua Bloch's
recommendations).

Does using generics make following this principle harder, or did the
original author of the above code simply not follow the rule?

Unless he wanted to have some ArrayList specific feature available (such
as clone()), there is no reason not to write

List<String> _hidden = new ArrayList<String>();
 
R

Roedy Green

This code violates the principle that you should declare your variables to
be interface types, if at all possible (as per Joshua Bloch's
recommendations).

I have heard that and seen it many times in other people's code. My
gut reaction is it is a not so hot a thing to do. I have never heard
the rationalisation for it. I think you should give the compiler and
your readers all the hints you can about what you are doing, not be
needlessly vague. It is not as though you are building in the word
"ArrayList" in a dozen places.

The only thing I can think of going for Bloch's recommendation of
using Interfaces is it discourages you from needlessly using methods
specific to ArrayList when your could have used generic List methods,
thus leaving yourself open to changing the List implementation later.

I can see using Interface references for very complex classes where
you want to deliberately narrow the role of the class, but not for
something like an ArrayList that has nothing but List methods to speak
of.

In the JVM, interface references are much slower than class
references. I don't see using them preferentially without good
reason.

My complaint is with casts that glue the type of variables in tightly
by effectively embedding the type info in many places instead of just
the one declaration. My proposal to free things up is the generic
cast.

But to answer your question, you can say something like this with
generics which should keep Mr. Bloch happy.

List<String> animals = new ArrayList<String>( 50);
 
J

jan V

This code violates the principle that you should declare your variables
to
I have heard that and seen it many times in other people's code. My
gut reaction is it is a not so hot a thing to do. I have never heard
the rationalisation for it.

You really surprise me Roedy. Normally you're typically on the side of best
practice techniques, but not on this one. I've got no trouble saying that
you're wrong on this issue (some supporting args below).
I think you should give the compiler and
your readers all the hints you can about what you are doing, not be
needlessly vague.

It's the exact opposite of being vague, when I write

List materialsList = new ArrayList();

... I convey to the reader "I need a variable to store a List of Objects, and
for the moment I've picked the ArrayList implementation for this concrete
list... but the logical essence of this object is that it is simply a List."

If ArrayList turns out to be a poor concrete implementation choice in some
future, I can pick a different List implementation, and nothing else in my
code should change.
It is not as though you are building in the word "ArrayList" in a dozen
places.

In non-trivial applications that is exactly what *does* happen... which
means if you ever want to pick a different implementation type, you're stuck
having to change all the occurrences in your program.
The only thing I can think of going for Bloch's recommendation of
using Interfaces is it discourages you from needlessly using methods
specific to ArrayList when your could have used generic List methods,
thus leaving yourself open to changing the List implementation later.

"The only thing" ? That's a huge benefit. Absolutely massive. Also think
about using interface types in method or constructor parameter declarations.
What do you think is more valuable: a method called bla(List).. .or one
called bla(ArrayList). I'd rather have the first !
In the JVM, interface references are much slower than class
references. I don't see using them preferentially without good
reason.

Premature optimization Roedy? I can't believe I'm reading what you're
writing ! (I hold you in very high esteem, you see, that's why..)
But to answer your question, you can say something like this with
generics which should keep Mr. Bloch happy.

List<String> animals = new ArrayList<String>( 50);

Mr. Bloch, and me :)
 
O

Oliver Wong

jan V said:
List materialsList = new ArrayList();

I'm relatively neutral on this topic in the sense that main reason I use
"ArrayList materialsList = new ArrayList()" instead of "List materialsList =
new ArrayList()" is because I'm too lazy to put in two import statements
(even with the help of Eclipse, that's TWO quickfixes I have to apply
instead of one). This is especially true for local variables that "no one
will ever see", as opposed to when I have to code public interfaces which
"must never change" (in which case I *would* use List over ArrayList, so as
to give myself as much room as possible).

That being said, doesn't the "List materialsList = new ArrayList()"
solution increase dependencies in the sense that your code now relies on two
types (i.e. List and ArrayList) instead of just one (ArrayList)?

- Oliver
 
R

Roedy Green

places.

In non-trivial applications that is exactly what *does* happen... which
means if you ever want to pick a different implementation type, you're stuck
having to change all the occurrences in your program.

I don't see that.

In the normal case you have code like this:

ArrayList l = new ArrayList()
or
List l = new ArrayList();

or possibly
l = new ArrayList();

If you change your mind you have no more places to go looking if you
are explicit in your type.

But you have opened the possibility to code like this which you did
not intend:

List l = new ArrayList();

l = new Vector();

which is simply an error.
 
O

Oliver Wong

Roedy Green said:
But you have opened the possibility to code like this which you did
not intend:

List l = new ArrayList();

l = new Vector();

which is simply an error.

I don't see how

List l = new ArrayList();
l = new Vector();

is any more of an error than:

ArrayList l = new ArrayList();
l = new ArrayList();

or:

List l = new ArrayList();
l = new ArrayList();

or any other combination of the above. Could you clarify what you mean
by "which is simply an error"?

- Oliver
 
R

Roedy Green

"The only thing" ? That's a huge benefit. Absolutely massive. Also think
about using interface types in method or constructor parameter declarations.
What do you think is more valuable: a method called bla(List).. .or one
called bla(ArrayList). I'd rather have the first !

I grant you the benefit LOOKS juicy, but I don't think you get a
payoff except in team programming. The cost far outweighs the payoff.

If I use List, it effectively mildly ties my hands behind my back
refusing to use all the methods of ArrayList. The odds are any gives
ArrayList variable 99% will stay an ArrayList forever. If I wanted to
change it, the time to pay the penalty is when I make the change, not
ahead of time for a highly theoretical possible change.

For Maps, that is another matter. Those you often want to flip around
to experiment with which one gives best performance later, or may have
ordering imposed later or removed later. But even then, it is
unlikely you would have used a specific non-Map method without reason.
Why pre-solve the problem of avoiding it before you have to?

In a team effort, obviously presolving it is much easier than
post-solving it. When you work by yourself post-solving is not that
much harder, and very rarely is there any need to solve it at all.

I like explicit types. I can very rapidly find out what anything is.
I don't like having to examine procedural code to be sure.
To me that is a practical benefit I am not willing to give up for a
theoretical flexibility benefit.

I thought perhaps there must be some other reasons for the Bloch
style. I could not imagine people giving up the knowledge benefit so
easily.
 
R

Roedy Green

Premature optimization Roedy? I can't believe I'm reading what you're
writing ! (I hold you in very high esteem, you see, that's why..)

Deliberately choosing a bubblesort when you have a perfectly good
quicksort is not premature optimisation. It is just common sense. You
do things the fast way when it is no more effort. You don't put on
blinders.

Listen literally to what I said:

"In the JVM, interface references are much slower than class
references. I don't see using them preferentially WITHOUT GOOD
REASON."

They are equally easy to specify. Why choose the slow one without good
reason?

I still don't think a case has been made for blanket adoption of the
Bloch practice. It has a huge drawback in comprehension and program
correctness.
 
R

Roedy Green

You really surprise me Roedy. Normally you're typically on the side of best
practice techniques,

Best practices are a set of tradeoffs for a particular coding
environment. The environment Joshua Bloch is used to at Sun is quite
different from the one I work in most of the time.

There is a difference between:

1. being sloppy
2. being ignorant
3. having a different set of values for the various tradeoffs.

I have never been one for following herd just to avoid ridicule. I was
promoting the idea of class libraries way back in 1979 when the idea
was considered grounds for confinement in a mental institution. We
STILL don't have the business classes I proposed. We do at least have
Calendar.

Consider Bloch's experience with teams containing perhaps some
relatively inexperienced coders who often use the wrong classes, and
often do things in round-about ways using quirky features of those
classes. He has time and time again had to prune the barnacles off
code to make it easy to flip to a new class. It bugs him how much of
Sun's code is hard coded as Vector than can't easily be flipped to
ArrayList because too much was exposed to the outside world. He
cringes to see someone using an ArrayList an a parameter. That locks
Sun in forever. They CAN'T change their code since so much other
people 's code depends on it, through calling and extending.

In my world, I produce mostly complete programs. I feel very little
constraint about changing anything at any time. Most of the time there
is just me. My "rules" are flexible since they don't have to be
followed by people who don't understand the principles behind them.
 
T

Thomas Hawtin

Roedy said:
If I use List, it effectively mildly ties my hands behind my back
refusing to use all the methods of ArrayList. The odds are any gives
ArrayList variable 99% will stay an ArrayList forever. If I wanted to
change it, the time to pay the penalty is when I make the change, not
ahead of time for a highly theoretical possible change.

I often use Lists other than ArrayList. Did I not just see you use
Arrays.asList? How about List.subList? Collections.unmodifiableList?
Collections.emptyList? Collections.singletonList?

Isn't it much easier to read if it just says List everywhere?

Tom Hawtin
 
G

Guest

That being said, doesn't the "List materialsList = new ArrayList()"
solution increase dependencies in the sense that your code now relies on
two types (i.e. List and ArrayList) instead of just one (ArrayList)?

ArrayList depends on List anyways, so you don't introduce any new
dependencies by declaring your variable as a List and assigning an
ArrayList to it.

La'ie Techie
 
O

Oliver Wong

Roedy Green said:
I like explicit types. I can very rapidly find out what anything is.
I don't like having to examine procedural code to be sure.

I think that if the idiom of "using the interface List instead of
ArrayList" is employed correctly, you shouldn't need to find out what the
object is, other than it is a List.

- Oliver
 
J

jan V

But you have opened the possibility to code like this which you did not
intend:
List l = new ArrayList();

l = new Vector();

which is simply an error.

At first sight this may look like an error, but it isn't, if you're thinking
in "proper OO" terms, i.e. think polymorphically. Declaring l to be a
reference to a List means just that, for the whole lifetime of the
reference. This means l could be switched to point to a different object, as
long as it is still a List, which is precisely what you're doing in the
above example. Nobody ever says that a reference should point to one object
for its entire lifetime... code is littered where references that get to
point to numerous distinct objects.

Like yourself, I initially went "Ugh, Roedy's right on this one." But after
reflection, I really don't think it is an error at all. Think of Java's
Hotspot compiler... it can decide to transform a normal bytecode method into
some native machine code at any time, and make the substitution "under your
nose"... your code doesn't care. As long as the semantics have not been
altered.

Now think back to your ArrayList to Vector switch... that's kind of similar,
isn't it? Your l reference now points to a different object (different
implementation), but it's still a reference to a List, which is is precisely
what we declared it as. So where is the problem? There is none.

Although I can not remember ever having used such a technique, you've just
pointed out a potential run-time optimization technique a la HotSpot. An
application could "switch" implementations on the fly, to suit the
circumstances.
 
J

jan V

I grant you the benefit LOOKS juicy, but I don't think you get a
payoff except in team programming.

In team programming .. and in during solitary efforts. My own library has
grown to a sizeable codebase over the years, and the use of
interface-centric field/argument type declarations makes my classes a lot
more flexible than they would otherwise be.
If I use List, it effectively mildly ties my hands behind my back
refusing to use all the methods of ArrayList.

What methods do you use in ArrayList that are not already specified by List?
Only if there are important methods in this list does that argument of yours
have much weight.
The odds are any gives
ArrayList variable 99% will stay an ArrayList forever. If I wanted to
change it, the time to pay the penalty is when I make the change, not
ahead of time for a highly theoretical possible change.

That is a good argument, I admit.
For Maps, that is another matter. Those you often want to flip around
to experiment with which one gives best performance later, or may have
ordering imposed later or removed later. But even then, it is
unlikely you would have used a specific non-Map method without reason.
Why pre-solve the problem of avoiding it before you have to?

I guess you're aligning yourself with the XP philosophy here: don't engineer
anything to do anything which isn't immediately needed. I don't buy that
philosophy, though I like a lot in XP otherwise...
In a team effort, obviously presolving it is much easier than
post-solving it. When you work by yourself post-solving is not that
much harder, and very rarely is there any need to solve it at all.

I think most people are really concerned with the most realistic scenario,
i.e. team work (though I'm not dismissing lone coders, I've done a lot of
that myself, but when we're talking software engineering, we're really
talking about teams working on software - realistically speaking)
I like explicit types. I can very rapidly find out what anything is.
I don't like having to examine procedural code to be sure.
To me that is a practical benefit I am not willing to give up for a
theoretical flexibility benefit.

I thought perhaps there must be some other reasons for the Bloch
style. I could not imagine people giving up the knowledge benefit so
easily.

I respect your point of view, and your arguments, though I think Bloch's are
stronger, plus I'm pretty much welded to that style of programming, so we'll
have to agree to differ on this one.
 
D

Dale King

Roedy said:
Deliberately choosing a bubblesort when you have a perfectly good
quicksort is not premature optimisation. It is just common sense. You
do things the fast way when it is no more effort. You don't put on
blinders.

Listen literally to what I said:

"In the JVM, interface references are much slower than class
references. I don't see using them preferentially WITHOUT GOOD
REASON."

They are equally easy to specify. Why choose the slow one without good
reason?

Becuase it reduces coupling which leads to more flexible designs.
I still don't think a case has been made for blanket adoption of the
Bloch practice. It has a huge drawback in comprehension and program
correctness.

I think there are various cases to consider with differences in benefits.

The most important case to consider is in a public interface (i.e. a
return value or parameter). There is no question here that you want to
use the least specific class here. If you choose too specific a class
you can be tieing your hands or the hands of the client of then
interface. Consider if you have an interface that returns an ArrayList
instead of a List. You may then be unable to switch to another
implementation of List. You may think that you can just change the code
that calls it. But who says you own the code that is calling it. Someone
else may control the code calling your public interface. If you take an
ArrayList parameter then you prevent a client from passing a LinkedList.

From there you just get more localized cases such as an instance
variable all the way down to a local variable. You are right that there
isn't much difference with local variables. But with refactoring local
variables can easily expand beyond being local variables.

And as far as performance, I read a great answer to a similar argument
that occurs regarding C++. Some people avoid virtual methods unless
absolutely necessary because they are less efficient. But in reality the
difference is about the same as the amount for adding a parameter to the
method. When was the last time you heard a cry to reduce the number of
parameters for performance reasons?
 

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,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top