Abstractions live longer than implementations
In general the more abstract your design the longer it is likely to remain useful. So, since Collection is more abstract that it's sub-interfaces then an API design based on Collection is more likely to remain useful than one based on List.
However, the overarching principle is to use the most appropriate abstraction. So if your collection must support ordered elements then mandate a List, if there are to be no duplicates then mandate a Set, and so on.
A note on generic interface design
Since you're interested in using the Collection interface with generics you may the following helpful. Effective Java by Joshua Bloch recommends the following approach when designing an interface that will rely on generics: Producers Extend, Consumers Super
This is also known as the PECS rule. Essentially, if generic collections that produce data are passed to your class the signature should look like this:
public void pushAll(Collection<? extends E> producerCollection) {}
Thus the input type can be E or any subclass of E (E is defined as both a super- and sub-class of itself in the Java language).
Conversely, a generic collection that is passed in to consume data should have a signature like this:
public void popAll(Collection<? super E> consumerCollection) {}
The method will correctly deal with any superclass of E. Overall, using this approach will make your interface less surprising to your users because you'll be able to pass in Collection<Number>
and Collection<Integer>
and have them treated correctly.
Here is an eight page answer to your question:
http://tinlizzie.org/~awarth/papers/fool07.pdf
If i can try and coarsely summarize the problems with adding laziness, it's the corner cases.
There are a lot of caveats around side-effects. Consider, in your example, if the constructor had visible side-effects like bumping a global counter or doing I/O... It's hard to reason about when that would happen. Or consider even uglier side-effects about exceptions (they're thrown ... when you reference the lazy object?)
Just skip to section 6 in the above paper. (And admire all the type system formal logic on the pages you skip ...)
Best Answer
Definitely not evil and not a code smell in my mind. Data containers are a valid OO citizen. Sometimes you want to encapsulate related information together. It's a lot better to have a method like
than
Using a class also allows you to add an additional parameter to Bottle without modifying every caller of DoStuffWithBottle. And you can subclass Bottle and further increase the readability and organization of your code, if needed.
There are also plain data objects that can be returned as a result of a database query, for example. I believe the term for them in that case is "Data Transfer Object".
In some languages there are other considerations as well. For example, in C# classes and structs behave differently, since structs are a value type and classes are reference types.